Palabras sobre código

El blog de un programador en .NET

Historias de usuario: No es análisis de software

Luego de algún tiempo de transición a la agilidad, mucho me ha impactado que tiendo siempre a considerar el ejercicio de creación de historias de usuario como un proceso de análisis de software pero con otro formato.

Las historias de usuario, son simplemente registros de un deseo de el cliente, fraseado de una forma conveniente para recordar la conversación que se llevó a cabo cuando se hizo la solicitud.

Es el programador que luego tome este ítem para trabajar, el que debe realizar el análisis correspondiente, realizar el diseño correspondiente y la implementación.

Dejo esto solo como recuerdo para volver a leerlo más adelante.

Testers en un equipo ágil.

Este es un punto algo controversial y no poco discutido dentro de los círculos ágiles cuando se desean aplicar a un ambiente empresarial.

Comencemos por decir que el tester tradicionalmente es una figura antagonista para el programador; se ubica justo antes del paso a producción de una aplicación con el  propósito de asegurar la calidad del software para poder reducir los periodos de marcha blanca, tan costosos y difíciles.

Al otro lado del río, en los equipos ágiles, se promueve un desarrollo iterativo en el cual el usuario trabaja de manera muy cercana a  los programadores, junto a una agenda de publicaciones muy frecuentes para que el software tenga contacto con el mundo real lo antes posible.

Esto se puede entender porque el desarrollo en cascada nace desde la administración hacia los programadores, mientras que la agilidad nace desde los programadores hacia la administración. La administración tiende a ver al desarrollo de software como una linea de producción en donde se debe colocar un control de calidad, y cuando la metodología es en cascada, el control va al final. Los programadores por su parte ven a los testers como una resistencia para que el software pase a producción y muchas veces como un insulto a su inteligencia. Obviamente esto está exagerado y se presenta en muchos matices, pero es para ilustrar un punto: Es natural que en un lado los testers sean muy necesarios mientras en el otro lado se clasifiquen como desperdicio.

Pero analicemos un poco el rol real del tester. El trabajo de un tester es ponerse en el lugar de un usuario y utilizar las funcionalidades del software de tal manera que le hagan sentido al negocio y a los usuarios. Nuestra responsabilidad como programadores es entregar un software que tenga la calidad suficiente para que el tester y por lo tanto, los usuarios, puedan usarlo día a día. Se analiza desde percepción de rendimiento hasta usabilidad de las pantallas. Si el tester encuentra un bug en esta revisión es su deber rechazar el build y comunicar al equipo los detalles del error.

En la agilidad contamos con muchas herramientas de automatización en cuanto al aseguramiento de calidad: historias de usuario que definen requerimientos en lenguaje de negocio, pruebas unitarias guían el diseño interno de la aplicación y lo comprueban repetitivamente, pruebas de aceptación automatizadas basadas en las historias de usuario que garantizan cumplimiento, entre otras. Pero sobre todas estas herramientas están los principios sobre los cuales la agilidad se basa, especialmente la comunicación con el usuario, que al ser fluida y sin intermediarios garantiza un correcto entendimiento del negocio y los requerimientos.

Todo parece indicar que el rol del tester no tiene cabida en un mundo ágil, especialmente cuando la comunicación del usuario y el programador es tan fluida como para que el usuario siempre esté entregando feedback sobre la aplicación.

Pero hay un punto débil en este razonamiento. El disponer del usuario con tanta fluidez es imposible en la mayoría de los desarrollos empresariales. Los usuarios son personas productivas para la empresa cliente y por lo tanto el cliente es celoso del tiempo que invertimos con ellos. Pero no es posible desarrollar un software sin el usuario. Muchos de nosotros tenemos cicatrices con las que aprendimos esa verdad. Lo que necesitamos es un punto medio y ese punto medio es el tester.

El tester ágil es parte del equipo de desarrollo, muy cercano al product owner y a su vez cercano a los programadores. El tester participa de la escritura de historias de usuario y dirige la escritura de las pruebas de aceptación. En este esquema el trabajo del programador continúa siendo el entregar una historia desarrollada que cumpla las pruebas de aceptación. El trabajo del tester es probar la historia de usuario desde el punto de vista de un usuario, encontrar oportunidades de mejora, detectar problemas de usabilidad o inconsistencias con otras historias previas o futuras. (Un tester sigue sin poder hacer su trabajo si el equipo de desarrollo no entrega un sw de calidad suficiente para ser probado)

El tester tiene un acceso más frecuente a los usuarios que el equipo de desarrollo. Esto permite que una persona en el equipo esté enfocada en capturar las personalidades e idiosincrasias de los usuarios, pero sin eliminar completamente el acceso dirécto de los programadores a los usuarios. Podemos pensar en el tester como una especie de “cache” de los usuarios, una capa de abstracción.

Si el cliente permite acceso directo a los usuarios y a la vez permite que los usuarios tengan una aplicación en producción con cambios nuevos cada dos semanas, entonces tener un tester es efectivamente un desperdicio. Pero si cualquiera de estas facilidades (o las dos) es negada o reducida, entonces un tester permite tener un ciclo de feedback rápido y sano apesar de aquello.

Fundamentos de Programación 2: Capítulo 1, Calidad y Eficiencia

Capítulo 1 – Calidad y Eficiencia

“La calidad nunca es un accidente, siempre es resultado de un esfuerzo inteligente” – John Ruskin

Pragmáticamente, la razón por la que yo y (asumo) la mayoría de los programadores, estamos deseosos de aprender, es para mejorar la calidad del código que escribimos al igual que nuestra eficiencia. No conozco programador que ocupe tiempo aprendiendo nuevos acercamientos y tecnologías con el objetivo explícito de escribir peor código, o encontrar medios para trabajar más lento. Pero, ¿Cómo medimos la eficiencia y la calidad? ¿Qué cosa, como creadores, consideramos que son nuestros objetivos?

Aquellos del tipo administrativo, considerarían como medida de calidad puramente la satisfacción del cliente. Aun cuando es ciertamente importante, los buenos programadores son los interesados con mayores expectativas y también son los más críticos. Como DeMarco y Lister dijeron en Peopleware, Todos tendemos a vincular fuertemente nuestra auto estima con la calidad del producto que producimos – no con la cantidad del producto, sino con la calidad. Encuentro difícil poder expresar con palabras, desde la perspectiva de un programador, lo que significa la calidad; pero, estoy seguro que no solo muchos de nosotros coincidimos en la definición, sino que también podemos reconocerla fácilmente (o la falta de ella). Calidad es sobre encontrar una solución elegante para un problema – tanto a un nivel micro (comportamientos individuales) como macro (el sistema como un todo). ¿Qué es elegante entonces? Varía según el contexto del problema pero, generalmente, una solución elegante es tanto la más simple posible, como la más explícita y fácil de entender.

Mientras más programo, más me doy cuenta de algo realmente sorprendente. No solo la solución más sencilla es obviamente la más fácil de implementar, sino que también la más fácil de entender, mantener y cambiar. Las soluciones simples también tienden a tener mejor rendimiento que aproximaciones más complejas. Esto es particularmente sorprendente cuando te encuentras hasta las rodillas en un lío sobre trabajado en donde la intención original de flexibilidad, rendimiento y eficiencia son saboteadas por complejidad artificial. Esto no significa que esté a favor de la programación sin consideración por el diseño. En vez, creo que dada cierta experiencia y reflexión, un balance entre pensar por delante los problemas y el pragmatismo no solo es lograble, sino que también bastante natural.

Me gustaría decirte cuál es la respuesta. No es que no lo sepa. El problema es que tengo que anteponer una advertencia; sino corro el riesgo que me des un desprecio y dejes el libro aquí. Yo sé que muchos de ustedes está cansados de escucharlo, pero les suplico que me escuchen y reconozcan que probablemente no estoy diciendo lo que ustedes piensan que digo. ¿Ok? Entonces, el secreto para escribir código con calidad es asegurarse de que el código es testeable. ESPERA… no te vayas aun. No estoy diciendo que debas testear tu código (Ciertamente tampoco niego que se deba hacer) solo estoy diciendo que si, teóricamente, fueras a testear tu código, no debiera ser difícil. El código puede ser testeable sin efectivamente testearlo y al final, es a lo que debieras apuntar.

Por un largo tiempo pensaba que la solución era realmente así de simple. Escribir tests y la calidad del código iría incrementando. Pero más recientemente me he dado cuenta que no todos los tests son creados iguales y que es enteramente posible, si es que no común, tener realmente malos tests. Entender qué es lo que hace a un tests bueno, y por lo tanto qué es lo que hace al código testeable, viene con la experiencia y espero, junto con los siguientes capítulos, pueda ayudarte a esquivar algunas de las piedras que me he encontrado en el camino. Es por esto que mucho de este libro estará dedicado tanto a escribir tests como a la testeabilidad del código. No mezcles testabilidad como una metrica con escribir tests. Es como escribir un montón de código mantenible que no necesita mantenimiento. Es un objetivo, no una actividad. Ahora, es cierto que la mejor manera de descubrir que una solución es testeable es mediante la escritura de un test. Eso sí, espero poder convencerte de que le des un intento; en los últimos tres años el convertirme en un escritor de tests efectivo es una de las cosas que más me ha ayudado a elevar mi habilidad. Sin embargo, es enteramente posible ver un método o un sistema y estimar su testeabilidad, y por lo tanto, su calidad, sin escribir un test.

Testeabilidad como una medida de calidad

No te culpo si eres escéptico. Todo lo que puedo hacer es prometer que mientras puede se que gastemos algo de tiempo escribiendo tests, no voy a tratar de hacerte sentir que estás haciendo algo malo si elijes no hacer tests. También, puedo mostrarte un ejemplo sencillo de lo que quiero decir:

//Un ejemplo de código dificil de probar, y por lo tanto, de mala calidad.
private void LoginButton_Click(object sender, EventArgs e)
{
    User user = SqlServerDataStore.FindUser(UserName.Text, Password.Text);
    if (user != null)
    {
        Session["userId"] = user.Id;
        Response.Redirect("~/members/index.aspx");
    }
    else
    {
        ErrorMessage.Text = "Invalid UserName or Password";
    }
}

(Si esta fuera una pequeña aplicación desechable, podríamos perdonarla. Aunque también debo decir que en mi experiencia, programadores hábiles escriben código limpio sin importar la expectativa de vida para el código. Esto es probablemente por el hecho que todos conocemos código de prototipos que ha terminado con un periodo de vida vergonzosamente largo.)

Este trozo de código está lejos de la perfección. Si específicamente lo miramos desde una perspectiva de la testabilidad, podemos ver una cantidad de dependencias que lo harán difícil de testear, como por ejemplo SqlServerDataStore.FindUser y Response.Redirect, por nombrar algunas. Lamentablemente muchas de estas vienen incluidas en el framework, lo que es uno de los motivos por los que muchos programadores .Net tienen tan fuertes sentimientos en contra de WebForms. En este caso en particular, la practica imposibilidad de hacer tests me dice que este código va a ser difícil de cambiar y mantener.

Muchas de las mejores prácticas que escuchas por ahí, como YAGNI (you aren’t going to need it; no lo vas a necesitar), bajo acople y alta cohesión, puede ser medido mediante la observación del código y pensando cómo lo testearías. Un método que hace demasiado, potencialmente está violando tanto YAGNI como la alta cohesión, y requerirá una cantidad dolorosa de código inicial en un test, y probablemente va a ser muy fácil de quebrar.

Hay un acrónimo en la programación que se usa bastante: SOLID. Se refiere a cinco principios importantes del diseño orientado a objetos: Single responsibility principle (Principio de responsabilidad única), Open/close principle (Principio abierto/cerrado), Liskov substitution principle (Principio de substitución de Liskov), Interface segregation principle (Principio de segregación de interfaces) y Dependency inversion principle (Principio de inversión de dependencias). Estos son todos tópicos dignos de su propio capítulo, pero deberá bastar decir que muchos de ellos son algo ambiguos. ¿En qué momento un nuevo comportamiento o mejora es considerado una responsabilidad o en cuál se le asigna un componente? ¿Cuándo una interfaz se vuelve demasiado compleja? Estas son preguntas importantes y respuestas equivocadas van a tener consecuencias. La testabilidad y la experiencia son herramientas que puedes usar para resolver esta ambigüedad. (Un buen lugar para comenzar a aprender sobre SOLID es en la Wikipedia.)

Eficiencia: No tan simple como parece

Puede que aún no me creas sobre la testabilidad como métrica de calidad, pero confío en que crees que la calidad es un aspecto de suprema importancia. Otro de nuestros objetivos pragmáticos es la eficiencia, debido a que no debemos demorarnos mucho en producir un código de gran calidad. Uno esperaría que la eficiencia en cuanto a aprender y mejorar, fuera algo fácil de medir: puedo lograr XYZ ahora más rápido de lo que lo hacía antes, pero no es así.

Primero que nada, y lo más peligroso, es que los programadores no siempre nos damos cuenta de que hay mejores formas de hacer las cosas. Seamos honestos, muchos de los programadores rehúsa aceptar solo la posibilidad que exista. Es difícil para la gente, especialmente aquellos no muy apasionados sobre lo que hacen, el aceptar que hay mejores formas de hacer las cosas. No solo amenaza su comodidad, pero potencialmente sus carreras. Yo estaba en la escuela cuando Java iniciaba a ganar popularidad y quedé impresionado por lo cerrado de mente de los programadores que rehusaban aceptar que la recolección de basura automática, luego de corregir algunos menores problemas, podría ser algo muy útil (ni pensar en estándar). Ahora encuentro particularmente irónico ver programadores de Java reusando aceptar que los lenguajes dinámicos podrían convertirse en el nuevo Java (Especialmente dado que generalmente utilizan los mismos argumentos que los que usaban los programadores C++ contra Java)

Segundo, sin importar cuanto más eficiente es una tecnología o técnica, siempre serás más eficiente en lo que sabes versus lo que estás aprendiendo. Mi estrategia para sobrepasar esto es tomarme mi tiempo y reconocer que el proceso es una inversión de largo plazo. Además si eres astuto, probablemente puedas introducir algo de investigación en tu trabajo diario. Por astuto no quiero decir tramposo. Quiero decir que encuentres un proyecto secundario, no crítico. Aquel sistema de monitorización que querías hacer pero que nunca tuviste tiempo, o aquel prototipo que te solicitaron hacer. La aproximación exacta que tomes va a depender de tu forma de aprender. Todo lo que puedo decir es que no debieras esperar un incremento de productividad de la noche a la mañana.

En Este Capítulo

Eso fue mucho texto y poco código. Este capítulo sentó las bases para los que continúan, los que presentarán mucho código y ejemplos. Definimos lo que significa calidad para nosotros asi como las métricas que usaremos para medirlo (testabilidad). Miramos a la eficiencia y descubrimos que mientras parecía fácil de medir, no lo era. Toma un respiro y considera algo del código que has escrito recientemente. Si hiciste tests, ¿Qué podría haber hecho los tests más simples y que tuvieran menor posibilidad de fallar si el sistema cambia? Si no lo hiciste, ¿Puedes imaginar algún inconveniente o problema que enfrentarías si decidieras hacer tests ahora?

Fundamentos de Programación 2: Introducción

Introducción

Siempre me encuentro sorprendido por el número de programadores con los que me encuentro. Mucho de sus vidas lo dedican al trabajo del cual somos afortunados por tener la oportunidad de estar haciendo lo que amamos. Esta no es una propiedad única de los programadores, pero es lo suficientemente extraña para que la mayoría de la gente lo encuentre sorprendente. “¿Vas a casa y trabajas cada día?” nos preguntan. Solo nuestros más cercanos amigos y colegas apasionados entienden que trabajo tiene dos significados para nosotros, ninguno de los que es una esclavitud de 9 a 5.

Probablemente has notado que el entusiasmo y puje es una característica critica en un campo tan joven. La tecnología continúa cambiando a un paso acelerado, el campo está creciendo y los sistemas están cada día volviéndose más complejos. El resultado es una clara división entre programadores que aman su oficio y lo que no. Estas o bien leyendo, intentando y a veces fallando, o estás estancado en el pasado, probablemente ignorante de lo atrás que te has quedado.

Desafortunadamente, ser apasionado por lo que haces no siempre es suficiente. Todos tenemos distintas fortalezas y debilidades, diferentes formas de aprender y diferentes tolerancias al fallo. También tenemos el constante flujo de nuevas tecnologías que hacen difícil decidir en cual ocupar el tiempo para aprender. La verdad es que no soy un gran programador. Soy un buen programador que le gusta aprender (y enseñar) y que disfruta jugando con la tecnología. He visto personas aprender un lenguaje solo días cuando a mi me ha costado meses, o escribir algoritmos con los cuales pretendo estar de acuerdo porque simplemente no pude entenderlo. Las Fundaciones de la Programación es una serie pensada para ayudar a la gente como yo, gente a la que le interesa lo que hacen y lo queren hacer mejor, pero no están seguros como hacerlo.

Espero que este libro te ayude.

Respecto al Original

El libro original Foundations of Programming ha sido, bajo mis estándares, un éxito. Tal parece haber ayudado genuinamente a desarrolladores y servido a una real necesidad cada libro, el original y este, existen por separado. Son independientes el uno del otro. Sin embargo podría sugerir que si tienes en mente leer el original, lo hagas antes de continuar con este. Mis vistas han madurado y en algunos casos cambiado o clarificado. Creo que mucho puede ser obtenido en ver cómo he evolucionado en la programación.

Fundamentos de Programación 2: Traducción de Foundations of Programming 2

He decidido comenzar un proyecto interesante. Voy a traducir Foundations of Programming 2, un libro que aun está en trabajo, por Karl Seguin.

Karl también escribió hace algún tiempo la primera parte de este libro, y si desean la pueden acceder de manera gratuita en http://www.openmymind.net/FoundationsOfProgramming.pdf

Gracias a que Karl utiliza GitHub para este proyecto, es muy fácil comenzar a traducir su trabajo, incluso mientras lo realiza. Además la licencia permite no solo traducirlo, sino que compartirlo y distribuirlo de manera gratuita.

Estoy muy entusiasmado, debido a que la primera parte fue muy buena, además porque siempre me ha interesado a contribuir en la disponibilidad de material de calidad en la lengua castellana.

Mi ortografía no será la mejor ni tampoco mi redacción, pero ya pueden comenzar a ver mi trabajo, también en Github, mediante la dirección: https://github.com/davidlaym/Foundations-of-Programming-2 y la versión original la pueden ver en https://github.com/karlseguin/Foundations-of-Programming-2

Estaré publicando los capítulos en el blog a medida los termine, y si bien ya tengo el primero traducido, quiero hacer algunas revisiones de ortogragía y redacción antes de publicarlo.

La idea es obtener opiniones y dar a conocer este esfuerzo.

Actualización:

Puedes acceder a la versión en castellano del primer libro de Karl en http://www.scribd.com/doc/33303471/Fundamentos-de-Programacion-Traducido

Trabajos en hilos separados con BackgroundWorker (parte 2, reusabilidad)

En la parte 1 mostré como crear una barra de progreso que realizaba un trabajo de manera asíncrona mientras mantenía a el usuario informado de que se estaba realizando un trabajo. Todo eso fue hecho en forma de prototipo, y con varias cosas pendientes que mejorar.

En esta segunda parte me voy a enfocar en la reusabilidad del código,  explicando una mejor forma utilizar esta ventana, y voy a explicar el manejo de exepciones. Todo esto acompañado de pequeñas modificaciones al código del post anterior.

Leer más de este artículo

Aprendiendo WPF

Si el blog no ha sido actualizado recientemente, es culpa de WPF.
Cada momento libre que he tenido he estado dedicándolo ya sea a descansar o a aprender WPF.

Es desagradablemente complicado todo el esquema de trabajo que presenta, pero a la vez, en esa complejidad hay una riqueza enorme.
Sinceramente creo que es un diamante en bruto (muy en bruto) y con el tiempo van a llegar mejores herramientas.

Me recuerda mucho cuando daba mis primeros pasos en HTML, y luego de varios años aparecieron las técnicas AJAX junto a los mejorados browsers.

Cuando salga VS 11 (no el 10, el 11) estoy seguro que ya estaremos en camino de una consolidación de la plataforma.

Por ahora sólo lo veo como una herramienta rústica pero poderosa, que todavía exige una cantidad demasiado grande de conceptos y técnicas nuevas.

A continuación, les dejo un par de links a recursos que me han servido infinitamente en cuanto al aprendisaje de esta nueva tecnología (en inglés):

Conceptos básicos de WPF (inglés)

Patrón M-V-VM Explicado (inglés)

Actualización: De hecho he encontrado una discusión bien interesante en el sitio StackOverflow  #534205 (inglés) en donde tratan el tema de la migración de aplicaciones y desarrollo de aplicaciones de producción con WPF en el mundo real. Coincide bastante con lo que es mi punto de vista: Excelente momento para aprender, pero hay que esperar un tiempo prudente hasta que la tecnología madure.

Trabajos en hilos separados con BackgroundWorker (parte 1, prototipo)

Para ciertos sistemas, es necesario realizar trabajos que toman un tiempo considerable, como por ejemplo la lectura de archivos grandes, la generación de archivos, la comunicación con sistemas en la web.

Para estos casos, es necesario que la aplicación permanezca activa e informe al usuario que se está realizando el trabajo, ya que de otra manera el usuario puede confundir la demora con una falla.

Una forma que he descubierto funciona bastante bien, es cada vez que se generan estos procesos, levantar un pequeño dialogo, con una barra de progreso infinita, que lo único que hace es dar esa información al usuario de que se está trabajando.

Veremos como implementar esta solución en C# 3.5 , utilizando hilos de ejecución paralela. Leer más de este artículo

Bug Driven Development

No tengo que explicar que los bugs son una realidad y que dado el estado de nuestra profesión, son imposibles de evitar por completo.

Pero una cosa completamente aparte es entrar en una lógica de desarrollar características en los sistemas de manera mediocre y sabiendo que se dejan errores y posibles puntos de falla sin revisar.

Veo esto cada día,  y en lo que termina en en un desarrollo guiado por los bugs, en donde tarde  o temprano el proceso de programación deja de ser un contante progreso en características y se vuelve un 70% resolución de bugs y un 30% introducción de nuevas características, que a su vez se desarrollan de la misma mediocre manera.

Basta de esto, tomemos esta profesión en serio.

TDD (Desarrollo guiado por pruebas): Impresiones

Durante los últimos meses he aprendido mucho sobre TDD y todo lo que lo rodea  y he tratado de evangelizar en mi organización sobre sus beneficios y virtudes.

Sin embargo, en mi organización soy el único desarrollador con OO (Orientación a objetos) en la sangre, todos vienen de VFox o VB6, por lo que ya tengo ocupado mucho tiempo y recursos en evangelizar sobre OO y en resguardar que todas las cosas dentro de mis proyectos se realicen de acuerdo a los principios de la OO.

Y es aquí mi donde entra mi primera impresión. TDD es una arquitectura para sistemas contruidos en la base de OO. Para desarrollar una aplicación susceptible a pruebas unitarias e integración, se requiere utilizar grandes cantidades de conceptos de OO que muchos VB6 tradicionales considerarían completamente fuera de sus conocimientos e intereses.

Yo mismo he tenido que aprender una o dos cosas sobre OO, y reforzar otro par de conceptos antes de poder hacerme una idea global de lo que es TDD, de cómo diseñar para probar y de todos estos conceptos asociados, como el DIP (Patrón de inyección de dependencias por sus siglas en inglés).

Moraleja: Partir por la base siempre. Primero introducir OO, Luego introducir TDD, luego introducir esquemas de desarrollo ágiles.

Seguir

Get every new post delivered to your Inbox.