Frontenders, fanboys y fatiga de framework

Si la evolución tecnológica en el sector IT ya es rápida, probablemente la del entorno frontend y sus frameworks JavaScript sea la que se lleve la palma, tal y como puede comprobarse fácilmente.

Además, la elección del framework hace tiempo que dejó de ser una decisión estrictamente técnica para ser también comercial. En consultoría, ofrecer lo último en frameworks tiene un valor diferencial, lo que impulsa aún más la velocidad de cambio.

Por último, según aumenta la complejidad del lado cliente, también crece proporcionalmente la complejidad de los frameworks que deben gestionarla, con lo que el aprendizaje es más costoso.

Si sumas todos esos factores, actualmente resulta prácticamente imposible especializarse en todos ellos antes de que empiecen a quedarse obsoletos, obligándote a ser pragmático y a tener que escoger. El resultado final para los/as profesionales frontend es un síndrome conocido como fatiga de framework.

El síndrome se reconoce por los siguientes síntomas:

  • Ya que tu futuro profesional a medio plazo dependerá de cómo evolucione el framework (su nivel de aceptación, actualizaciones, apoyo de la comunidad, etc) y/o que tu empresa decida adoptarlo como estándar, y que dichos aspectos quedan fuera de tu control, suele generarse un alto grado de ansiedad a la hora de decidir en cuál vas a invertir tu tiempo y esfuerzo. Una vez escogido, ahora te ves impulsado/a –conscientemente o no– a defenderlo a muerte para justificar la inversión realizada, al margen de si se adapta o no a los requerimientos del nuevo proyecto. Bajo este sesgo de confirmación, es fácil convertirse en un fanboy, donde al final lo único que consigues es ser menos objetivo y que te adaptes peor al siguiente cambio.
  • A veces, el síntoma es justo el contrario: una reacción alérgica a su simple mención, que te impulsa a querer renunciar a todos ellos, pese a ser obvio que reducen el código que tendríamos que teclear nosotros mismos en caso de no usarlos.

Por tanto, aunque van a seguir siendo útiles y necesarios, quizá esta dinámica acelerada y tan framework-centrista hace que se descarten otros aspectos en los que formarse, mucho menos volátiles y que constituyen inversiones mucho más inteligentes de tu tiempo.

Aquí te propongo unos cuantos:

Aprende de patrones de diseño

En ingeniería de software se entiende como patrón de diseño a una solución optimizada para resolver un problema que se presenta una y otra vez.

Si no los conoces, te encontrarás reinventando la rueda en cada ocasión de forma poco eficiente.

El término surgió desde el mundo de la arquitectura tradicional y fue trasladado al desarrollo de software en un libro muy famoso escrito en 1995 por el grupo conocido como The Gang of Four (GoF).

Los 23 patrones del libro siguen totalmente vigentes, siendo tan sólo el punto de partida para otros muchos que han ido surgiendo desde entonces, adaptados a cada lenguaje y entorno.

Uno de los nuevos y de los más útiles para nuestros proyectos front es el patrón Flux (Redux es su implementación más conocida), que es la alternativa más interesante surgida en décadas al patrón MVC, para la gestión del estado en aplicaciones interactivas y que vino desde el equipo de React en Facebook.

Otro patrón reciente y de los más interesantes por su potencia, es el patrón Observable Stream de RxJS; lo que me lleva al siguiente tema:

Conoce la programación reactiva

Las extensiones Reactivas, o Rx, son un desarrollo de Microsoft, originalmente para C# y que ha sido portado a múltiples lenguajes desde entonces. RxJS es su versión JavaScript.

El concepto de programación reactiva gira en torno al patrón Observable Stream” (abreviado a Observable” habitualmente) y que es una combinación de los patrones “Observer e “Iterator del GoF.

El interés de RxJS reside en que permite resolver cierto tipo problemas –eventos que se repiten en el tiempo con un contenido semántico determinado- de una forma completamente diferente y, una vez que la conoces, mucho más compacta y elegante que de forma tradicional, con programación imperativa.

Ofrece un estilo más funcional y declarativo, donde defines un stream de sucesos de un determinado origen y al que luego puedes encadenar múltiples operadores de transformación (hay decenas).

Además, estos streams pueden bifurcarse en otros streams, o pueden combinarse entre sí, para al final admitir uno o múltiples suscriptores (u Observers) que consumen cada nuevo valor que es procesado.

Son los Observables los que llaman a los Observers, no al revés. Es una tecnología “push” donde los Observables invocan los métodos next(), error() o complete() de los Observers registrados, según se produzca un nuevo valor en el stream, un error o el stream llegue a su final, respectivamente.

Como lo más ilustrativo es un ejemplo, es típico cuando estás aprendiendo RxJS programar el autocompletado de un input, para rellenar un combo con las opciones más probables. Supón los siguientes requerimientos:

“Dispones de un servicio en tu backend que ha de utilizar como entrada lo que se vaya tecleando en el input, pero debes evitar hacer peticiones innecesarias, como hacerlas a cada pulsación (sólo cuando lleves sin teclear algunas décimas de segundo), tampoco si no cambia el texto tecleado (podrías estar pulsando el cursor moviéndote por el input) o si no dispones de al menos 3 caracteres”.

Piensa por un momento cómo lo harías y luego mira este código de tan sólo 10 líneas:

 1.   const observable$ = Rx.DOM.fromEvent(input, 'keyup')
 2.     .map(e => e.target.value)
 3.     .filter(text => text.length > 2 || text === "")
 4.     .throttle(300)
 5.     .distinctUntilChanged()
 6.     .switchmap(term => searchService.getAutocompleteValues(term))
 7.
 8.   const subscription = observable$.subscribe(
 9.     data => console.log(data.response)
10.     error => console.log(error)
11.   )

En (1) se define el stream, teniendo como origen los eventos “keyup” del input y aplicándole luego diferentes operadores (2-6). Todos admiten un stream de entrada y generan otro de salida. Al final, disponemos de un stream donde cada nuevo valor a su salida será un array de cadenas listas para rellenar el combo asociado al input.

Curiosamente, hasta que no haya un primer suscriptor, el stream no se activará, sin importar lo mucho que teclees.

En (8) se crea un Observer definiendo simplemente dos funciones, “next()” y “error()”, para gestionar el siguiente valor del stream o un posible error, respectivamente. En este caso no se define una tercera función “complete()” porque el stream es infinito.

A favor de RxJS está claramente su potencia y lo compacto que puede resultar, abriendo un mundo alternativo de soluciones si planteas tus requerimientos como streams, cosa que es aplicable a más situaciones de las que puedas creer ahora mismo.

En contra de RxJS está su curva de aprendizaje, porque la documentación oficial no es la mejor para los principiantes (aunque cada vez hay mejores alternativas) y donde la dificultad principal es aprender a pensar de forma diferente, temporal y más funcional.

La cantidad de operadores disponibles para transformar y combinar los streams es enorme y aún hay más conceptos por aprender, como distinguir entre streams “calientes” y “fríos” o los Subjects, que son Observables y Observers a la vez. Aprender a modelar soluciones basadas en todo ello es un reto a largo plazo.

Angular fue valiente al incluir RxJS como una de sus dependencias (y lo está pagando con una reputación de ser un framework difícil), haciendo que casi todos los elementos principales del framework dieran la opción de trabajar con ellos en modo Observable: el módulo HTTP, los cambios del Router y hasta los formularios pueden utilizarse como streams Observables y los templates tienen facilidades para suscribirse y de-suscribirse de forma automática a Observables que expongas en tus componentes, utilizando muy poco código (con el pipe async).

Por ello suele vincularse RxJS a Angular pero, al igual que Redux se suele vincular a React, realmente ambas librerías son proyectos independientes y nada impide usarlas con cualquiera de los otros frameworks (o con VanillaJS).

Dado que los Observables van a ser implementados de forma nativa en futuras versiones de JS y usando RxJS como implementación de referencia, parece un buen consejo ir empezando a escalar esa montaña de la programación reactiva.

Utiliza Typescript

Typescript es una máquina del tiempo al futuro de JavaScript. Se trata de un supra conjunto de ES5, ES6 y ES7 que puedes usar hoy. Cualquier código ES5 o ES6 es ya un programa Typescript. Si Babel se queda en ES6, Typescript va más allá y (opcionalmente) le añade tipos estáticos.

Y si los tipos estáticos te echan para atrás y te parecen más un obstáculo que una ayuda, déjame darte un par de pistas de alguien que ya pasó por esa situación tiempo atrás.

Los que venimos de ActionScript vivimos exactamente el mismo conflicto cuando se pasó de AS2 a AS3. ActionScript 2 era en sintaxis y filosofía muy similar a ES5: igualmente dinámico, con herencia basada en prototipos y sin tipos estáticos (AS, JS y TS son todos implementaciones de ECMAScript).

Entonces llegó una reescritura de la máquina virtual para aumentar su rendimiento y a ActionScript se le añadieron, entre otras cosas, clases y tipos estáticos.

Ni que decir tiene que se lió parda, especialmente por lo obligatorio del cambio. Lo que se decía por entonces entre los desarrolladores eran cosas como (quizá te suene de algo):

  • Me están cortando las alas. Me quita la libertad de decidir cómo quiero que sea este objeto a cada momento.
  • No veo para qué tengo que añadir estos tipos. No me aporta nada, excepto dolores de cabeza.
  • Los errores de compilación están en perfecto chino mandarín. Esto no compila ni a tiros. Me corta el flow.

Por supuesto la batalla la acabó ganado AS3, por dos motivos: salto brutal de rendimiento y mayor velocidad y calidad de programación.

¿Te resulta chocante escuchar que se acelera el desarrollo?

Cuando tratas con variables simples, tiparlas explícitamente puede parecer excesivo y de hecho lo es (en TS no es necesario).

En cambio, resulta ya más interesante tipar las estructuras de datos más complejas del proyecto, definiendo su schema válido (no tienes por qué hacerlo a mano).

Si lo combinas con un IDE que permita explotarlo, como Visual Studio Code o WebStorm, entonces el desarrollo se vuelve más sencillo y más fiable debido a que:

  • Dispones de Intellisense/autocompletado a la hora trabajar con esos datos, los parámetros de las funciones y lo que devuelven. Según tecleas puedes ver lo que está disponible, sin errores.
  • Ya no eres tú quien tiene que memorizar la estructura de tus datos, es el IDE quien lo hace y, por tanto, puedes gestionar estructuras datos más complejas con menos esfuerzo mental.
  • Si te han cambiado el schema desde el server sin que tú lo sepas, lo puedes detectar mucho antes y depurarlo también más fácilmente.
  • Se detectan muchos más bugs en tiempo de desarrollo, que ahora sólo son detectados en tiempo de ejecución.

Por ejemplo, quizá no sepas que Visual Studio Code es una aplicación Web híbrida programada en Typescript y que en Microsoft dicen que, habiéndola empezado en vanilla JS, nunca se habría podido completar sin saltar a TS y apoyarse en estructuras de datos tipadas.

Si tu aplicación es pequeña o mediana puedes mantener el control sin tipos, pero si es más grande o puede crecer…

También hablaba antes de un salto de rendimiento gracias al tipado: cuando no tienes tipos estáticos y, por ejemplo, creas un array de 10.000 números, actualmente el motor JS lo implementa en memoria mediante una lista enlazada, creando punteros a cada una de las 10.000 posiciones en el heap, que ha de reservar/liberar por separado (estoy simplificando un poco, pero no mucho).

Con tipos estáticos, cuando sabes de antemano que vas a guardar 10.000 números y sabes que son, por ejemplo, enteros de 64bits, el motor JS podría implementarlo reservando 80.000 bytes contiguos en memoria, que te aseguro que se manipularían y recorrerían a mucha más velocidad que mediante una lista enlazada.

Usando Typescript el rendimiento del motor JS no va a cambiar hoy en día, porque TS únicamente se transpila a ES5 o ES6, pero al igual que pasó con la máquina virtual Flash (todo iba 10 veces más rápido en AS3), si se quiere dar un salto en prestaciones en la máquina virtual del navegador, una manera de hacerlo puede ser añadiendo tipos de forma nativa (¡se admiten apuestas!).

Mientras tanto puedes usar Typescript con cualquiera de los tres frameworks como un compilador de ES6 y usar tipos de forma progresiva (¡que no es obligatorio usarlos para todo!) cuando experimentes las mejoras que ofrecerá el IDE y el poder gestionar datos más complejos con menos esfuerzo.

Usa Web Components

Cada framework aporta ya su propia componentización, pero sólo existe una solución que es agnóstica a cualquiera de ellos y que se puede usar en todos: el estándar Web Components.

Realmente consiste en una combinación de cuatro tecnologías: custom elements, HTML templates, el shadow DOM y HTML imports, que pueden utilizarse también por separado.

Aunque todavía están en proceso de adopción por todos los navegadores (hay polyfills mientras tanto) el objetivo es generar nuestros propios elementos HTMLs, de forma que sean indistinguibles para el navegador de otros nativos. Esto permite que puedan usarse dentro o fuera de cualquier framework, de forma transparente.

A nivel de arquitectura, una manera de clasificar los componentes es dividirlos entre “listos” y “tontos” (smart/dumb o structural/presentational).

Los componentes “listos” serían los que son conscientes del estado de la aplicación, en el sentido de que, en aplicaciones MV*, escuchan los cambios en los modelos de dominio o, en aplicaciones tipo Flux, tienen acceso al Store.

Los componentes “tontos” serían los contenidos por los otros componentes, siendo configurados únicamente mediante sus atributos.

“Componentes de presentación” me parece mejor nombre que el de “componentes tontos”, porque sugiere que no van a ser complejos y no tiene porqué ser así. Me imagino tener que construir desde cero un componente de calendario con mucha funcionalidad y puede ser de todo menos simple, a pesar de ser configurable únicamente mediante sus atributos.

En tu aplicación, no importa cuál sea el framework, podrías sustituir los componentes de presentación por Web Components estándar.

Cada vez hay más soluciones para acelerar su desarrollo sin tener que pegarse directamente con esos APIs nativos. La que lleva más tiempo es Polymer, pero recientemente han surgido  otras muy interesantes como Stencil.js, de parte del equipo de Ionic y que permite compilar Web Components estándar a partir de una combinación de JSX + Typescript.

Ionic estaba muy vinculado en origen a Angular y con este proyecto pretende independizarse de frameworks concretos, para aumentar su cuota de mercado al volverse utilizable en todos ellos.

Incluso los mismos frameworks están haciendo cosas muy parecidas: con la versión 6 de Angular, mediante lo que han llamado Angular Elements, puedes igualmente compilar y publicar Web Components estándar.

No parece mala idea que les copies la estrategia.

No seas un fanboy

Me voy a centrar en los tres frameworks que lideran los rankings de stars de github, de conversaciones de cuñados y de hits de Google Trends, aun sabiendo que todo lo que diga probablemente quede obsoleto antes de que acabes el post.

A fecha de publicación son Angular, React y Vue.

Angular nace con vocación de framework, mientras que React o Vue, si bien inicialmente eran únicamente soluciones para las vistas, en su evolución han ido estableciendo diversos stacks de librerías que los convierten en verdaderos frameworks.

Si tratamos de fijarnos más en sus similitudes que en sus diferencias te das cuenta de que todos ellos:

  • Se basan en una arquitectura de componentes. Las aplicaciones se forman instanciando un árbol de componentes anidados.
  • Disponen de grandes empresas detrás impulsándolos (React-Facebook y Angular-Google) y/o de ecosistemas con el tamaño suficiente con todo el apoyo necesario. Vue es únicamente un proyecto soportado por la comunidad, pero con gran crecimiento y mucha disposición.
  • Tienen varios UI kits que aceleran la construcción de interfaces de usuario.
  • Disponen de herramientas de línea de comando (CLIs) que aceleran el desarrollo, algo especialmente útil con los que requieren de más boilerplate (hablo de Angular).
  • Después del cambio de licencia de React ahora ya son todos de uso libre y sin incertidumbres legales para tus proyectos.
  • Pueden generar aplicaciones móviles híbridas avanzadas, casi nativas. No hablamos de las basadas en WebViews a toda pantalla como Cordova o Ionic, sino en verdaderos UIs nativos, eso sí, orquestados desde un motor JS:
    • React Native para React únicamente, que es el más popular a día de hoy.
    • NativeScript, que se puede programar desde Angular o Vue o VanillaJS y que tiene otras ventajas.
  • Pueden renderizar en el servidor, con el objetivo de mejorar la carga inicial y el SEO.
  • Pueden implementar una gestión de estado tipo MV* tradicional o una de tipo Flux.
    • En React, Redux es la más usada, si bien soluciones MV* no se ven apenas y hay otra alternativa popular, MobX, con un enfoque intermedio.
    • En Angular se puede usar Redux sin problemas pero es más frecuente NgRx, que es una mezcla de Redux + RxJS y que usa las mismas herramientas de desarrollo de Redux, permitiendo integrarse mejor si ya haces uso de los Observables del resto del framework.  
    • En Vue tienes MVC por defecto y de tipo Flux tienes sobre todo a Vuex.

Si bien cada framework tiene sus particularidades e incluso dogmas de funcionamiento, si te centras más las similitudes que en las diferencias, lo cierto es que en todos se pueden hacer cosas parecidas.

Además, se percibe una convergencia creciente entre ellos y las transferencias mutuas de tecnologías son cada vez más frecuentes.

En definitiva, que en mi opinión hay conocimientos más fundamentales y menos volátiles en los que invertir tu tiempo, que te van a dar un mayor valor como profesional y que te van a permitir adaptarte más rápidamente a la inevitable evolución del frontend.

¿Estás de acuerdo?, ¿no? ¡Espero tus comentarios!

Con 14 años descubrí que, tecleando ciertas cosas, podías hacer aparecer en la pantalla lo que quisieras y desde entonces no he parado. Tras estudiar informática en la Universidad Pontificia de Salamanca me especialicé en el diseño y desarrollo de sistemas multimedia, tanto dentro como fuera de la pantalla, siempre interesado en la experiencia de usuario final. Actualmente soy uno de los arquitectos del departamento Front-end de Paradigma Digital.

Ver toda la actividad de Arturo Batanero

Escribe un comentario