Sobre la elección del lenguaje
En todo proyecto de desarrollo de software los arquitectos deben, tarde o temprano, tomar ciertas decisiones o enfrentarse a elecciones que pueden despertar polémicas; decisiones referentes a aspectos tales como la tecnología de persistencia de datos, los middlewares, los frameworks (léase aquí: librerías), tecnología de la capa de delivery, granularidad de los servicios, estrategias de despliegue y un larguísimo listado de etcéteras. Pero de entre todas las elecciones, la que quizás resulte más arriesgada es la elección del lenguaje de programación, y no me refiero a elecciones del tipo Java vs C#, de hecho difícilmente alguien pueda dudar de estos lenguajes. Tampoco resultaría extraña la utilización de PHP, ni de Python o Ruby, ni de Javascript con NodeJs aunque otros, como Scala o Lua, podrían requerir cierta justificación en algunos entornos.
Cuando digo que no generarían demasiado ruido me refiero a que son muy buenos lenguajes, bien conocidos, bien documentados, con una enorme comunidad y miles y miles de sistemas han sido desarrollados con ellos. Pero, ¿qué sucede si decidimos que el mejor lenguaje para el proyecto que le vamos a desarrollar a nuestro cliente es, por ejemplo, GorillaScript, Sugar o Fun? Es muy probable que la resistencia sea lo suficientemente fuerte, tanto desde dentro como desde fuera del equipo, como para desistir y volver a PHP, Java o C#. Es más, en algunos casos extremos, como el del cliente para el que estamos desarrollando algunas aplicaciones actualmente, el temor a un nuevo lenguaje de programación ha sido históricamente tan grande que desarrollaron todo su sitio web en COBOL! (Pero eso será motivo de otro post)
Coffeescript
La elección de CS no es algo que podamos considerar arriesgado ya que no es más que un dialecto de Javascritpt y compila a Javascript. No obstante, en un entorno corporativo, a diferencia de lo que puede suceder en un proyecto Open Source o en nuestro Pet Project, apostar por este lenguaje requiere de coraje. En parte porque si el proyecto fracasa (por las razones que sean) una de las primera preguntas a las que el arquitecto puede ser sometido es la siguiente: “Coffeescript?! Pero en qué diablos estabas pensando!? ”. Por otra parte, todos los que eventualmente se hubiesen opuesto, ya sea por temor al cambio, o simplemente por preferencia personal, podrían sacar leña del árbol caído; a veces sucede.
Por cierto, una eventual justificación acerca de por qué encarar un nuevo desarrollo con este lenguaje se vuelve algo complicado puesto que, aún hasta el día de hoy, existe toda una controversia a su alrededor. Controversia en la que prominentes personalidades del mundo Javascript no terminan de ponerse de acuerdo.
Coffeescript en el mundo real
Después de meditar por meses sobre los pros y contras, y sobre cómo podría solucionar algunos problemas que tuvimos en los desarrollos anteriores con javascript, me puse a buscar sobre experiencias en el mundo real y lo único que encontré fueron Trello y Dropbox, y obviamente un millar de librerías y aplicaciones open source, algunas muy buenas por cierto.
Eso me pareció suficiente puesto que, por cada aplicación mundialmente reconocida en internet, existen miles de otras aplicaciones bien hechas y brindando soluciones reales solamente que no son tan conocidas. Así que me decidí: “¡Vamos con Coffeescript!”
Pero ¿por qué?
En muchos proyectos web veo que se repite lo mismo una y otra vez: programadores para los que javascript no es su principal lenguaje y que no saben javascript o, en el peor de los casos, programadores que no saben que no saben javascript. Lo que sucede en estos casos es que mientras que el código del servidor es decente, el del cliente suele ser una colección de cientos o miles de snippets de jquery traídos desde StackOverflow.com y una librería javascript por cada problema que se les presenta.
La solución es evidente: deben aprender javascript. ¿Pero cómo? ¿con una capacitación? ¿con un libro? ¿con guías de desarrollo? ¿con JLint? No. Definitivamente no. Incluso muchos patrones javascript requieren un entendimiento profundo de la naturaleza prototipal del lenguaje, los temas relacionados al scope de las variables en las funciones y closures requiere también atención extra, aprender las diferencias de uso de los operadores y las funciones de alto orden también suelen resultar dificultosas. Es simplemente demasiado.
CS soluciona estas y muchas (pero realmente muchas, muchas) cosa más, permite escribir un código más claro, conciso, con menos defectos, más mantenible, en menor tiempo (al menos una vez que ya nos hemos familiarizado con él), incorporando patrones comunes y bien conocidos de manera gratuita, generando código javascript que funciona en prácticamente todos los browsers, etc. La idea no es enumerar las bondades aquí.
Pero una imagen vale más que mil palabras, asi que, ¿alguien me explica como se pueden mantener 13.000 LoC como las que siguen? Advertencia: es código real.
Primer proyecto con Coffeescript
Ya a punto de terminar con el desarrollo de la aplicación y viendo hacia atrás creo que la elección no sólo ha sido correcta sino que ha superado mis expectativas. Me explico, la aplicación es una SPA desarrollada con Knockoutjs, JQuery y CS; Knockoutjs posibilita (o fuerza) la utilización del patrón MVVM por lo que tenemos Views, ViewModels y Models por separado, obviamente los últimos 2 son 100% CS y al ser una SPA requiere de todos los conceptos que típicamente implementamos con otros lenguajes: OOP (sí, encapsulamiento, herencia y polimorfismo) y AOP, tracing, exception handling, organización mediante namespaces y módulos, unit tests, además de una lógica de negocios harto compleja (con una interfaz de usuario que la acompaña).
Esto es algo distinto al desarrollo de una aplicación web tradicional con ASP.NET MVC en el que nuestro lenguaje es C# y en donde las vistas utilizan Razor, aquí nuestro lenguaje es javascript, y la lógica de la interfaz de usuario está en el cliente.
En este escenario no valen los snippets jquery ni que todo sea global (obviamente esto no vale en ningún desarrollo, pero es muy común), aquí necesitamos código Javascript de alta calidad, bien limpio, que incorpore las mejores prácticas utilizadas con este lenguaje, que sea orientado a objetos, que permita la inyección de dependencias, que sea sencillo de leer y modificar, que sea modular.
Hay quienes podrían argumentar que todo esto se puede lograr igualmente con javascript ya que ninguna de las consideraciones anteriores tiene relación directa con CS. Eso es tecnológicamente cierto pero técnica y humanamente falso, me explico:
-
Tecnológicamente hablando, todo lo que se puede hacer con C# 5.0 ya se podía hacer con C# 4.0 y este, no introduce nada que no se pudiera hacer con C# 3.0 y así podemos llegar a la conclusión de que todo lo que se puede hacer con C# 5.0 ya podía hacerse con C# 1.0. Y aunque esto es cierto, no deja de resultar ridículo ya que no es lo mismo programar una operación sobre colecciones genéricas con Linq que programar bucles anidados para operar sobre colecciones de objects.
O como decían los programadores de assembler: “Todo lo que se puede hacer con C, se puede lograr igualmente con ensamblador y por lo tanto, C no trae nada nuevo”. Este es el argumento que más resuena en la red y, aunque cierto, es absolutamente estúpido.
-
Técnica y humanamente hablando, no es lo mismo escribir y mantener 6.500 LoC que 10.000 LoC y esto se vuelve aún más evidente a medida que este número crece, es decir que no es lo mismo mantener 650.000 LoC que 1.000.000 LoC. Pero es incluso mucho más fácil mantener 65.000 LoC en un claro CS que 1.000.000 LoC de un ruidoso JS.
Pero mejor una imagen de código real de nuestro proyecto:
¿De cuál lado prefieres trabajar? ¿Cúal te gustaría mantener? Personalmente prefiero trabajar sobre las 19 líneas de código CS de la izquierda (muy claras y sencillas de modificar) que con las 74 líneas de JS de la derecha. Y si bien es cierto que yo no escribiría el código de la derecha del mismo modo que lo hace el compilador de CS, también es cierto que luego de algún esfuerzo y un buen refactoring, el código JS escrito a mano seguiría siendo todavía más ‘verboso’, ruidoso y duro de mantener que su equivalente CS del lado izquierdo. ¿Puede apreciarse ese ruido?
El equipo
Nuestro equipo se conforma de un desarrollador .NET, un Líder Técnico puede dedicar aproximadamente un 50% de su asignación a la programación de la aplicación y soporte (o incluso menos) y yo, que puedo dedicar entre un 10% y un 30% como mucho a soporte y programación. Testing y diseño web se requieren en momentos puntuales del ciclo de vida y, aunque no es así como trabajamos habitualmente, por restricciones de contrato y presupuesto no nos quedó otra alternativa.
Este equipo debía desarrollar la aplicación en 2 meses, y si no fuera porque el alcance fue ampliándose, esta semana estaríamos terminándolo. El punto aquí es que solo yo contaba con alguna experiencia previa con CS así que debíamos aprenderlo sobre la marcha y muy, pero muy rápido.
Aprendiendo Coffeescript y algunos problemas
CS se aprendió sobre la marcha y aún estamos aprendiendo nuevas cosas todos los días porque el lenguaje tiene muchas más features y caminos que los que se describen brevemente en el sitio CoffeeScript.org. Pero cada vez que aprendemos algo nuevo inmediatamente cambiamos el código en aquellos lugares en donde hicimos algo “casero” y que CS resuelve de manera más conveniente.
Pero esto no fue feliz al comienzo. Al principio del proyecto hubo cierta resistencia por parte del TL y del desarrollador en cuanto al lenguaje, y es lógico que eso sucediera puesto que es frustrante pasar 8hs tratando de realizar una tarea trivial y no lograrlo por no conocer el lenguaje. Y no sólo el lenguaje, KOJS era también nuevo para los dos, era realmente esperable. Encima, los desarrolladores se sienten presionados por mostrar resultado rápidamente y el no poder desempeñarse como quisieran por culpa de un cóctel de tecnologías nuevas genera un desagradable estrés.
Fue necesario acompañar el proceso de aprendizaje, asistirlos y aclararles lo evidente: nadie espera que aprendan todo esto en unas semanas. Y aunque eso no reduce la frustración, al menos queda claro donde están las responsabilidades y cuáles son las expectativas que se tiene ellos. Otro punto importante es que el desarrollador hacía lo correcto y luego lo hacía correctamente, eso también ayuda.
Por último, y no de menor importancia, es el hecho de que revisando y depurando el código generado, el equipo todo ha aprendido mucho más sobre javascript y algunos patrones comúnmente utilizados de lo que tal vez lo hubiera hecho programando directamente con javascript.
Lecciones aprendidas
Solo algunas:
-
Mantener los archivos .coffee pequeños hace más sencilla la tarea de depuración y correlacionar nuestro código con el javascript generado.
-
No hacer demasiadas cosas en una sola línea. Aún cuando CS posibilita (y avaces parece que alentara) la escritura de sentencias larguísimas, éstas se vuelven difíciles de leer y mantener, ni que hablar de comparar y mergear. Simplemente hay que pensar que el código debe ser legible no sólo para nosotros y listo.
-
Usar una herramienta que permita ver el JS generado mientras se escribe CS o al menos revisar el código generado con frecuencia. De esta manera se evitan varios dolores de cabeza puesto que a veces el código generado no es lo que esperábamos simplemente por un typo en nuestro código CS o un espacio/tab mal puesto. De paso se familiariza uno con el código ‘real’ que se ejecutará.
-
Configurar el editor (en nuestro caso Visual Studio) para que muestre los espacios en blanco y tabulaciones. Esto es importante porque muchas veces, sin quererlo, se mezclan espacios y tabs y uno debe saber donde es que están. Lo mismo sucede cuando el editor está configurado para hacer “word wrapping”, uno no sabe si es una línea nueva o si simplemente es la continuación de la línea anterior.
-
Los UTs no son opcionales. A medida que se escriben más y más líneas de código, el refactoring se vuelve mucho más riesgoso y uno termina rompiendo funcionalidades inesperadas que luego solo pueden detectarse en tiempo de ejecución.
-
Integrar los UTs con el entorno de desarrollo y correlos a cada rato, luego de una modificación en cualquier parte correrlos a todos. Luego de actualizar la copia local del repositorio correlos a todos, antes de subir algo correlos a todos. Escribir UTs en CS (o JS) es infinitamente más sencillo y rápido de hacer que en C# o Java así que, a menos que hagas TDD (no es nuestro caso), escribí tantos casos como puedas. No te vas a arrepentir.
-
Dedicar parte del día a leer algo sobre CS. Siempre vas a encontrar una manera mejor de hacer algo que hiciste.
Herramientas
Desarrollamos con Visual Studio así que utilizamos las siguientes extensiones para trabajar con CoffeeScript:
Mindscape Web Workbech
Esta extensión brinda el coloreado de la sintaxis CS y genera compila (genera el equivalente JS) cada vez que se guarda un archivo. Solo es cuestión de escribir algunas líneas y hacer Ctrl+S para ver cómo queda nuestro código. Esta extensión es una buena alternativa y no hemos tenido problema alguno con ella.
Chutzpah Test Adapter for Test Explorer
Esta extensión permite que Visual Studio detecte la existencia de tests escritos en CS y permite correrlos desde Visual Studio de la manera habitual, haciendo fácil es escribir algo de código y correr los test, todo en el ambiente integrado de VS. Para esto, compila y corre nuestros tests con PhantomJS utilizando la librería que queramos (Jasmine, Mocha o QUnit). Chutzpah hace lo que promete, y lo hace muy bien, pero por desgracia cuando falla un test uno no puede simplemente poner un breakpoint para ver qué pasa, ni crea un code map para saber al menos en qué línea CS está fallando.
Por esta razón yo hubiese preferido correrlos desde el browser pero bueno, aún así la herramienta vale la pena y nos está dando buenos resultados.
Aquí puede verse un test sencillo:
Y aquí puede verse la suite corrida:
Conclusión
- CS es un lenguaje que llegó para quedarse porque los beneficios que trae consigo son reales y evidentes para todos los que se quitan las anteojeras ideológicas de javascript y lo evalúan desde una posición de neutralidad.
- La incorporación de CS puede generar alguna frustración y penalidad de productividad en las primeras semanas si no se lo conoce de antemano.
- CS puede ser aprendido, en un nivel suficiente para realizar un desarrollo, en un período de tiempo muy corto. Si se conoce bien javascript, el pasaje es sencillo; de lo contrario se van aprendiendo algunos conceptos y patrones javascript observando y depurando el código generado (pero primero JS y luego CS, lo opuesto no es válido).
- Las extensiones para Visual Studio funcionan bien y permiten el desarrollo completo de una aplicación sin tener que cambiar de editor de texto para codificar ni cambiar de entorno para correr las pruebas unitarias.
- CS es simplemente JS.
- No pieso volver a escribir javascript nunca más en mi vida.