Palabras sobre código

El blog de un programador en .NET

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?

Advertisement

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s

Seguir

Get every new post delivered to your Inbox.