Cómo sobrevivir a Javascript (e incluso disfrutar con él)

Javascript se le atraganta a mucha gente. Como lenguaje y como plataforma. A estas alturas todos conocemos sus peculiaridades como lenguaje y, aunque le tenemos especial cariño a la coerción de tipos y al significado de this, esas partes de Javascript son más o menos manejables una vez que te acostumbras.

Lo que más nos cuesta, al menos a los que estamos más acostumbrados a otro tipo de lenguajes y plataformas, es la filosofía detrás del lenguaje y la forma en que evoluciona el ecosistema de librerías, frameworks y herramientas. Nos empeñamos en tratarlo como si fuese Java o C#, y como me dijeron hace tiempo:

Javascript está bien cuando no lo tratas como el hermano retrasado de Java

En este post vamos a ver una serie de consejos que nos permitan sobrevivir al desarrollo con Javascript e incluso llegar a disfrutar con él. Que sí, en serio. Desarrollar con Javascript puede ser divertido (e incluso productivo).

Camiseta I Survived Javascript
Camiseta original de Koalite’s Kipple

Aprovecha el lenguaje

Como decíamos antes, Javascript tiene un montón de errores catastróficos de diseño rarezas que necesitas conocer para no volverte loco. Pese a ello, si conoces su filosofía, por muy cuestionable que te parezca, podrás intentar sacarle partido.

Javascript es un lenguaje dinámico, con prototipos en lugar de clases y con un cierto enfoque funcional (depende de con quien hables te dirá que es un lenguaje funcional o te mirará raro por insinuarlo). Aprovéchalo. Aprovecha esas características de Javascript y úsalas en tu favor en lugar de intentar replicar continuamente los diseños que haces en Java o C#.

No te empeñes en crear “clases” para todo y declarar 40 funciones constructoras como si fuesen Java Beans o DTOs; utilizar objetos literales es una opción perfectamente válida.

No necesitas simular un mecanismo de herencia entre clases, los prototipos sirven y, muchas veces, ni siquiera necesitas eso. Ya sabes, composición en lugar de herencia. Y no te olvides de que hay otras técnicas, como los mixins que pueden dar lugar a diseños muy limpios.

No hace falta que todo forme parte de un objeto. Puedes tener funciones sueltas y muchas veces es la mejor manera de implementar una funcionalidad. Además las funciones son ciudadanos de primer nivel en Javascript, asegúrate de usar eso en tu favor y no compliques innecesariamente tus APIs recibiendo objetos complejos cuando una función basta.

Vigila el acoplamiento y mantén la cohesión

Acoplamiento y cohesión son dos conceptos fundamentales siempre que desarrollamos software, pero en el caso de Javascript, por ser un lenguaje dinámico, resultan especialmente importantes.

Tener un alto grado de acoplamiento entre componentes del sistema implica, entre otras cosas, que los cambios en un componente se propagan a muchos otros. Esto no sólo ocurre cuando cambia el comportamiento del componente, sino también con cosas más triviales como cambios en su API (renombrar una función, modificar sus parámetros, etc.).

Este tipo de cambios en Javascript son más complicados de realizar porque no tenemos un compilador para validar que hemos actualizado las distintas invocaciones del componente, por lo que debemos ser muy cuidadosos al realizarlos. Reducir las dependencias entre componentes será de mucha ayuda en estos casos.

Si buscamos un diseño con componentes muy cohesionados que nos permitan agrupar claramente la implementación de una funcionalidad concreta, será más sencillo mantener los cambios localizados en cada componente y minimizaremos las dependencias entre ellos.

Elige bien las herramientas que vas a utilizar

Hay quien se atreve a decir que Javascript tiene las mejores herramientas de desarrollo. Yo no diría tanto, pero es cierto que actualmente existen muy buenas herramientas para desarrollar con Javascript.

Aprende a manejar las herramientas de línea de comandos de node.js que pueden simplificar tu desarrollo, automatiza la “compilación”, utiliza algún linter para validar tu código, ejecuta tests, etc.

Busca un buen gestor de dependencias, incluso si trabajas sólo para frontend. Puedes utilizar módulos npm con browserify, irte a algo más flexible (y complejo) como webpack, a algo exótico como jspm o cualquiera de las opciones existentes, pero utiliza alguno.

Existen IDEs para trabajar con Javascript, como WebStorm, NetBeans, Eclipse… (incluso creo que hay gente que usa Visual Studio), pero mi recomendación es que, aunque acabes usando un IDE para editar, comprendas cómo funcionan las herramientas que hay por debajo y seas capaz de ejecutarlas sin necesidad del IDE, porque seguramente éstas avancen mucho más rápido que tu IDE y quedarte atado a él te va a acabar limitando.

Al tratarse de un lenguaje dinámico, vas a perder parte de las estupendas herramientas de refactorización a las que seguramente estés acostumbrado, pero a cambio vas a ganar herramientas para mejorar la experiencia en tiempo de ejecución, como recarga automática de la aplicación (incluso manteniendo el estado en algunos casos), REPLs, etc.

Échale un vistazo a la oferta disponible, revisa unos cuantos proyectos open source para ver cómo trabajan ellos y busca un flujo de trabajo con el que te sientas cómodo y productivo. No te limites a seguir usando las mismas herramientas que usabas para programar en Java o C# porque tal vez no sean las más adecuadas para trabajar con Javascript. Piensa que los que más desarrollan en Javascript no suelen usar Eclipse ni Visual Studio, y a lo mejor es por algo.

Mucho ojo con las dependencias externas

Si unimos los puntos anteriores (lenguaje dinámico, bajo acoplamiento, herramientas) con un ecosistema inestable vibrante en el que aparecen nuevas herramientas, frameworks y librerías cada semana, y donde los que se mantienen cambian APIs sin pensar siempre en la compatibilidad hacia atrás, llegamos al que para mi es ahora mismo el factor clave para sobrevivir Javascript y no volverse loco: ser muy consciente de las dependencias que se asumen, sus versiones y el impacto que tienen en la aplicación.

Hay que tener cuidado tanto con las dependencias de código (librerías) como con las herramientas de desarrollo (sistemas de compilación, de tests, etc.), porque al final ambas van a condicionar la forma en que trabajamos.

Cuando empezamos a depender de librerías (con frameworks es aún peor), tenemos que intentar aislarnos al máximo de ellas minimizando la superficie de contacto entre nuestro código y el suyo, para que cuando haya cambios en sus APIs, no nos toque rehacer la aplicación entera. Esto también se aplica si queremos sustituir una librería por otra, que es un objetivo muy loable pero tal vez no sea algo tan habitual (el cambio de APIs, desgraciadamente, es más habitual de lo que me gustaría).

Es exactamente el mismo escenario que al cuidar el acoplamiento entre nuestros componentes, pero con un riesgo mayor porque nosotros no controlamos el ciclo de vida de la librería. Obviamente no es lo mismo asumir una dependencia sobre jQuery que sobre la última librería de moda que lleva 3 meses de desarrollo (y por tanto es mucho más inestable), pero aun así, intenta que tu aplicación sea lo más independiente posible.

Además, hay que intentar evitar librerías con dependencias complicadas entre ellas. Si nuestra aplicación depende de A, B y C, pero además A depende de C y B depende de C, cuando queramos actualizar C tendremos que asegurarnos de que hay versiones de A y B compatibles con la nueva versión de C. Por supuesto, cuando esto lo llevas a 8-10 librerías con versiones cruzadas, es todo mucho más divertido.

Con las herramientas pasa algo parecido. Necesitas actualizar la versión del lanzador de tests para corregir un problema, pero resulta que el plugin de tu sistema de ejecución de tareas con es compatible porque el reporter que se usa para generar el fichero que luego lee el servidor de integración continua no está actualizado. Triste, pero habitual:

I switched from 6to5ify@3.1.2 to babelify@6.1.3 and at the same time upgraded browserify from 6.3.4 to 10.2.6, watchify from 2.3.0 to 3.2.3 and remapify from 1.4.3 to 2.0.3. Now I get several build errors: ‘import’ and ‘export’ may appear only with ‘sourceType: module’

Por eso, en ambos casos hay que pensar muy bien qué dependencias se están asumiendo, lo estables que son, y las dependencias cruzadas que están generándose. Lo mejor, claro está, es usar pocos componentes externos, que sean muy estables, que no afecten a muchas áreas de nuestra aplicación y que no dependan unos de otros, pero todos sabemos que eso no es siempre posible y hay que buscar un equilibro entre reinventar la rueda y montar un engendro de librerías cogidas con pinzas que se nos caiga en cada cambio de versión.

Otro factor que ayuda a gestionar las dependencias es fijar las versiones que estamos utilizando.

Por defecto, npm tiene la mala costumbre de confiar semantic versioning y permitir que las versiones que instalemos de cada dependencia cambien siempre que sean compatibles unas con otras. Lo malo es que no todo el mundo respeta sematic versioning tan bien como debería, y aunque lo hiciera, siempre estás expuesto a bugs, por lo que todo es mucho más sencillo si eres tú el que decide cuándo vas a actualizar y a qué versión.

Ojo, para esto no basta con poner versiones fijas en el fichero package.json porque con eso fijas las versiones de tus dependencias, pero no las versiones de las dependencias de tus dependencias, que pueden (y suelen) estar definidas con cierto rango de tolerancia. Si quieres asegurarte de mantener las mismas versiones, necesitas utilizar un comando como npm shrinkwrap o (¡sacrilegio!) incluir tus dependencias externas en el control de código de fuente.

Resumen

Posiblemente si llegaste a Javascript desde Ruby, Python, PHP, o alguna plataforma parecida, todo lo que he contado te parezcan obviedades que llevas haciendo desde hace un montón de tiempo. Para mi, que llegaba desde una plataforma como C#/.NET, con su lenguaje estático, su IDE monolítico y sus librerías estables (esto ahora está cambiando -para mal-), asumir esta nueva forma de trabajar me ha llevado un tiempo.

Al final, todo se reduce a aprovechar las características del lenguaje, aplicar conceptos básicos de programación, utilizar las herramientas adecuadas y valorar los riesgos que supone depender de componentes de terceros.

En realidad no son cosas nuevas, son principios básicos a la hora de desarrollar software, pero en otros entornos contamos con herramientas (en el sentido más general de la palabra) que nos permiten ocultar nuestro mal hacer a la hora de desarrollar.

13 comentarios en “Cómo sobrevivir a Javascript (e incluso disfrutar con él)

  1. Tienes toda la razón, uno de los principales problemas de los developers que entran a Javascript cuando se viene de un lenguaje tipado es querer tratarlo como tal y más cuando se viene con el modelo de toda la vida de herencias por clases y olvidándose que la herencia no solo se puede realizar mediante clases sino tambien usando prototyping.

    Para colmo con la salida de Typescript (el cual me gusta bastante, pero..) creo que no resuelve el problema, solo lo oculta trata de decirle a los devs no te preocupes por aprender herencia por prototype, solo preocupate por herencia entre usando clases.

    Desde mi punto de vista es importante que un developer javascript se preocupe por entender javascript tal cual como es con su virtudes y defectos.

    Debe adaptarse a usar las Herramientas para Javascript no quere usar Javascript con mis herramientas de toda la vida adaptarse al cambio, muchos quiere que todo funcione dentro de Visual Studio o Eclipse, que todo sea como las clases osea no tener que cambiar y ahi es que esta el problema.

    A mi a nivel personal me ha costado bastante adaptarme a Javascript y a su forma de ser y trabajar con el, todo fue mejor desde que empeze a usar javascript como javascript y trabajar con sus herramientas no con las herramientas diseñadas para otros lenguajes que se adaptan a javascript.

  2. Muy buena foto del panorama actual. Con tanto «haz ésto pero cuida con aquello… usa eso pero no mucho… prueba éste y sino aquelle…» me ha venido a la cabeza el hombre orquesta XD XD XD

  3. Soy consciente de que lo que voy a decir puede resultar, a primera vista, digamos… controvertido.

    Una de las principales cosas que señalaría yo es esta: Presta especial atención a con quién formas equipo para trabajar sobre JavaScript. Aunque lógicamente esto puede ser aplicable a cualquier otro lenguaje, plataforma, entorno, etc, mi experiencia es que en el caso de JavaScript es particularmente importante. No tengo ningún otro dato que mi propia experiencia anecdótica, claro, y eso quizá suponga una perspectiva limitada, puede ser, pero aún así confío en que esa anécdota es relevante.

    El problema es que existe, alrededor de JS, mucha desinformación. Y esto ha llevado a mucha mala información y a muchos desarrolladores sin suficiente experiencia que han adoptado costumbres, supersticiones y creencias en lugar de buscar conocimiento real. Es demasiado fácil encontrar mala información con apariencia de ser buena.

  4. Lo siento pero no estoy de acuerdo contigo, JavaScript es el hermano retrasado de Java, y no es culpa de Netscape, el inventor de JavaScript (la palabra Java en el nombre no es casual), sino de Microsoft, Microsoft en IE 6 congeló JavaScript impidiendo evolucionar incluso cuando el nuevo y flamante Mozilla primero y FireFox después presionaran a Microsoft en el sentido contrario. Microsoft es el culpable principal de que JavaScript haya estado momificado durante décadas y no haya evolucionado a «mejor» mucho antes.

    El objetivo de Netscape con JavaScript no era nada ambicioso, su finalidad era poco más que rellenar formularios, e interaccionar con applets, cuando se inventó la capacidad de modificar el DOM en su momento era bastante mierdera pero por limitaciones del navegador Netscape, muy lejos de las ambiciones que tenemos ahora.

    No buscaban hacer un lenguaje completo y bien diseñado, el problema es que se haya quedado durante décadas … EN ESO.

    Los prototipos son la chapuza que introdujo Netscape para crear a toda prisa un lenguaje que permitiera algo parecido a «clases», sin herencia claro.

  5. Respecto a «usar JavaScript como Java» pues si, confieso que hace años (muchos) un tal «Yanis no me acuerdo» alias YASD, mostraba una serie de técnicas para hacer OOP de forma similar a Java, y gracias a eso y alguna idea propia, me siento «aceptablemente» agusto con JavaScript, puedo estructurar bien el código que por desgracia suele ser mucho más complicado que un Hello World, puedo hacer herencia, polimorfimo, llamadas a métodos «super», incluso «mixins» (herencia múltiple) y evitar en lo posible el código spaghetti que invita a hacer JavaScript.

  6. Flynn,

    Estoy de acuerdo en lo que comentas y creo que es algo bastante frecuente en todos los lenguajes «populares». No creo que haya más desinformación en Javascript que, por ejemplo, en Java o PHP. Quizá en Javascript se agrava por lo rápido que ¿avanza? la comunidad, lo que hace que, si ya cuesta encontrar información decente, encontrarla actualizada es casi misión imposible.

    Jose María,

    Javascript tiene muchas limitaciones y su diseño deja mucho que desear, pero creo que nunca ha pretendido ser Java.

    Tener clases o herencia o llamar a métodos «super» no hacen que un lenguaje sea mejor. Te pueden gustar más o menos, ser más o menos útiles en determinados contextos, pero yo no diría que son características que hacen objetivamente mejor a un lenguaje (si es que tiene sentido siquiera hablar de lenguajes objetivamente mejores).

  7. Si, reconozco que mi código JavaScript apesta a Java pero es la única forma que me gusta para poder hacer código bien estructurado

    bit.ly/1TL3hNv

  8. Afortunadamente, como bien dices los que venimos de Python, como un servidor, tenemos este tipo de cosas más interiorizadas.

    Yo creo que lo que tira para atrás a mucha gente de JS es que el ecosistema avanza demasiado rápido, pero pienso que esto es un problema del desarrollador, no de la plataforma. Se puede utilizar un stack de herramientas y bibliotecas con 1 o 2 años de antigüedad, bastante estables y documentadas. Pero sí que requiere dedicación estar al día.

    Con ES6 o simplemente con CommonJS pienso que se puede tener un código bien ordenado, otra cosa es que el lenguaje no te obligue a ello. Eso para unas cosas está bien y para otras no tanto.

    Un saludo

  9. Enhorabuena por el post y por la recopilación de enlaces.

    A mi lo que me jode del ecosistema javascript es la habilidad para defenestrar herramientas y expulsarte de la corriente dev cool, de la que siempre quise formar parte. ¿Grunt o gulp? Bueno, grunt esta más estabilizado, Gulp está casi empezando…pasa un año…todo los gurús se han pasado a gulp. Me paso a gulp…pasa un poco más de un año…coño, ahora es scripts npm. Y usaremos bower, que el pack grunt, bower, yeoman mola. Y pasa un año y «bower sucks». Y como buen borrego a migrar a npm. Angular es la hostia. Venga, a tragarme todos los videos de egghead. Monto mi app y al mes de terminarla…Angular es «lento», mejor React. Cuando dije hasta aquí fué cuando leí que browserify YA NO, AHORA es webpack. A tomar por culo.

    El ultimo proyecto lo hice con gulp, browserify para organización de código, lodash (extend) para composición de objetos, jquery para ajax y alguna manipulación dom, handlebars para templates y nada de frameworks. Y bower por joder, no se a quien, pero por joder.

    Y tras terminarlo me sentí sucio, por lo que aproveche una oferta de udemy para un curso de Flux. Y en esa estamos…

  10. Excelente el comentario, ndesorden.

    Es uno de los mejores consejos para sobrevivir a Javascript: déjate de modas y elige en función de tus necesidades, no de lo cool que parezca cada cosa ;-)

Comentarios cerrados.