Este artículo también se encuentra publicado en inglés.

Me cuesta darle un fin a mis tareas de programación.

Antes de la PR (Pull-Request) o asignarlas a QA (Quality Assurance), vuelvo a abrir los archivos de código. Reviso línea por línea. Especialmente las partes donde tuve que hacer compromisos, dónde no me cerró cómo hice las cosas, pero no se me ocurre qué mejorar. Y pienso: “Si solo supiera más, si tuviera más experiencia, podría quedar más óptimo, más elegante, más legible…”. Por supuesto que en algún momento hago la PR, en mi equipo solemos cerrar todas las historias al final del Sprint, pero en algunos casos mi cabeza no quiere aceptar el estado “Done” de una tarea y guarda el problema en un rincón oscuro.

Y después de semanas, o tal vez meses, leo un artículo, miro un video, escucho una explicación y mi cabeza dice: “¡Ya sé!” No siempre puedo volver al código y mejorarlo, pero ahora soy una mejor programadora. Conozco una nueva forma de solucionar un problema.

Cuando empecé a aprender sobre Remix, ya venía usando Next.js hace unos dos años. También tengo experiencia con Gatsby. Leyendo la documentación de Remix tuve tantas experiencias “¡Ya sé!” que sentí la necesidad de compartirlas. Como un carpintero necesita una variedad de herramientas para construir productos específicos de forma eficaz, las y los profesionales de la programación necesitamos conocer patrones, técnicas, librerías y frameworks con el fin de que nuestro código sea escalable, mantenible y longevo.

A continuación, me gustaría presentar el proceso de investigación realizado:

Etapa 1: coleccionar información

Para entender por qué Remix es una herramienta útil a tener en cuenta a la hora de elegir las tecnologías para un proyecto, hay que entender las desventajas de las demás tecnologías disponibles el día de hoy.Acá un resumen de mi propia experiencia en conjunto con la información que obtuve sobre Remix.

Ventajas y desventajas de SSG

En el caso de Server Side Generation son páginas web estáticas en su mayoría. Se realiza un build de la web y luego se despliega. Una de las herramientas comúnmente usadas para la construcción de sitios estáticos es Gatsby. La ventaja de los sitios estáticos es que pueden ser distribuidos a través de un CDN, Netlify por ejemplo, y de esa manera es posible reducir el time-to-first-byte (TTFB) para el usuario, logrando mejor performance. Los problemas empiezan cuando queremos realizar pequeños cambios, como por ejemplo arreglar un error ortográfico o agregar un banner en la página de home. Después de la modificación, hay que volver a realizar el build y redistribuir el contenido. Dependiendo de la complejidad del sitio y también de la cantidad de imágenes que haya que optimizar en este proceso, eso puede llevar un tiempo considerable: Kent C. Dodds comenta en su charla que su tiempo de build de página web era tan largo que producía un timeout en Netlify.

Para resolver el problema de tener que hacer un build nuevo de todo el sitio para arreglar un solo error ortográfico, se implementó ISR - Incremental Static Generation, que permite sólo buildear la parte del sitio que realmente cambió.

También es posible agregar datos dinámicos a sitios estáticos: solo hay que obtenerlos del lado del cliente: una vez descargado el documento estático al browser, podemos obtenerlos con javascript desde una API e insertarlos en el DOM. Esto puede producir un salto visual, ya que cuando apenas se carga la página, esos datos todavía no son visibles y recién se introducen pocos segundos después al documento. CLS (Cumulative Layout Shift) es una de las Core Web Vitals para calificar el impacto de esos saltos visuales en la experiencia de usuario y debemos evitarlos. Si la altura del contenido dinámico es conocida, se puede prevenir el salto con un loader o un skeleton para una mejor experiencia de usuario.

Ventajas y desventajas de SSR

Server-Side Rendering es la respuesta a los problemas con SSG: cuando un sitio tiene tantos datos dinámicos que no es factible implementar un sitio estático, se recurre a generar la página del lado del servidor con cada request. Un framework muy usado basado en React que soporta SSR es Next.js. Con SSR resolvemos los problemas de buildeo, ya que la web se genera de manera dinámica cada vez que un cliente la pide. Siempre y cuando obtengamos toda la información necesaria del lado del servidor, también evitamos el problema de los saltos visuales. Sin embargo, perdemos las ventajas que nos daba el CDN: al no poder almacenar nuestro sitio en un CDN, los tiempos de carga de la página pueden empeorar dependiendo de la ubicación del servidor. La optimización de los tiempos de respuesta del backend juega otro rol importante.

Remix: the best of both worlds

  • Remix aprovecha la infraestructura distribuida de servicios como Cloudflare Workers o Fly para hostear una aplicación SSR lo más cercano del usuario posible y así lograr la misma velocidad de un CDN.
  • Al desalentar obtención de datos del lado del cliente, no hacen falta herramientas como React Query, SWR o Apollo en la página, lo cual implica menor bundle-size, menor ancho de banda ocupado por un sitio web y por ende ahorro de datos móviles para los usuarios que navegan el sitio con su teléfono.
  • A su vez, al renderizar todas las páginas del lado del servidor, se evitan por completo los saltos visuales (CLS).

Hay varios aspectos más donde Remix tiene una ventaja en performance a otros sitios:

  • Muchos sitios no son responsivos hasta que no se terminó de cargar todo el javascript. Esto puede dejar confundido al usuario que en los primeros segundos de la carga de la página intenta ingresar un texto en un campo de búsqueda sin que éste responda. La filosofía de Remix es facilitar el desarrollo de un sitio que usa javascript para mejorar la experiencia de usuario, no para habilitar el sitio. Eso implica que inputs, menús o botones sean responsivos de forma inmediata.
  • En comparación a Next.js, Remix es capaz de hacer prefetching de sitios dinámicos, por ejemplo, en el caso de búsquedas por texto ingresado por el usuario obteniendo resultados que parecen casi instantáneos. Eso es posible gracias a que el cliente como el servidor en remix tienen conocimiento de todas las dependencias del sitio completo y por ende saben exactamente qué renderizar. Frente a una búsqueda, los resultados se preparan entre que el usuario termina de escribir y hace click en el botón de “buscar”.

Etapa 2: Probar la tecnología

Con todo lo aprendido, procedí a indagar más profundamente en la documentación y armé un repositorio para poner los conceptos a prueba. Para un aprendizaje práctico, Remix provee un tutorial dónde se arma una aplicación full-stack desde cero. Con la ayuda de dicho tutorial, creé un repositorio propio donde experimenté con los siguientes conceptos:

  1. File based nested routing: ayudandome con la documentación de React Router como también en el tutorial de Remix, una parte de la aplicación sirve para jugar con los conceptos de ruteo anidado de Remix, el cual permite configurar y encajar varios layouts diferentes. Si, por ejemplo, todo mi sitio tiene que tener footer y header, pero, a su vez, todos los blogs tienen que tener una lista de tags en un menú al costado, Remix me permite componer ambos layouts para el dominio /blogs.

Captura de Pantalla 2022-09-12 a la(s) 14.18.29.png

  1. CSS modular y Tailwind: en Remix, los stylesheets se importan parecido a como se importan en html, con link tags. No se recomiendan soluciones CSS-in-JS para que la interactividad de las páginas ni los estilos dependan de javascript. A su vez, es posible importar los estilos en rutas particulares sin que interfieran con el CSS de otra ruta: si la página /blogs tiene su propios estilos, los nombre de las clases no van a interferir en otra página /productos. También probé con Tailwind, que lo pude configurar en pocos minutos gracias a la excelente documentación de tailwind.
  2. Para mostrar datos obtenidos de una API en nuestra página, hace falta escribir un loader que se ejecuta del lado del servidor. Los datos se pasan al cliente con un hook especial de Remix: useLoaderData. Implementé dos loader sencillísimos: uno para obtener datos vía REST y otro para Grapqhl. En ambos casos usé la API pública de Rick and Morty para probar con una API real.

6 (1).png

Un data-loader que obtiene charactéres de la API REST de Rick y Morty.

1 (2).png

Un data-loader que obtiene characteres de la API GRAPHql de Rick y Morty.

Una de las particularidades de Remix consiste en cómo maneja formularios, ya que es posible ejecutar todas las acciones necesarias del lado del servidor. Con esto, del lado del cliente evitamos el useState-hook para los inputs, el eventHandler y por ende no hace falta usar la función event.preventDefault()! Y cómo ese formulario funciona completamente sin javascript, funciona en el momento en el que carga la página web.

3 .png

El action-handler de un formulario que recibe una tarea con el nombre de la persona asignada a la tarea.

  1. Error Boundaries: el manejo de errores para condiciones imprevistas en las aplicaciones es difícil, más de una vez descubrimos errores en preproducción porque la pantalla se ponía en blanco. La causa: un error de javascript. Para resolver ese problema React 16 implementó un concepto llamado ErrorBoundaries que permite capturar errores en el componente y mostrar un mensaje de error en lugar del componente. Remix mejoró las ErrorBoundaries para que también capturen errores en el código server side. De esta forma, la pantalla no se pone en blanco por completo sin que usuario tenga oportunidad de entender qué pasó. Con los ErrorBoundaries, el usuario solo ve un error en una parte de la página, pero otras partes de la página no se ven impactadas, por ende el usuario puede seguir navegando.

2 .png

5 (1).png

Si te interesa ver el código completo, acá está el repositorio.

Etapa 3: una charla interactiva

Con todo lo que leí y probé, tenía muchas ganas de compartir con los demás lo que me parecía Remix y en qué situaciones podía ser útil. Publiqué el repositorio con mis pruebas y armé unas filminas para dar una charla. El día de la charla, los chicos podían clonar el repositorio y modificar el código mientras yo explicaba. Me parecía importante una experiencia “hands-on”, para un mejor aprendizaje pero también para que los que participaron se lleven código funcional y así puedan seguir experimentando. Para los que no pudieron asistir de forma presencial, transmitimos la charla en vivo por Youtube.

Si te interesa, podés verla acá.

Conclusión

En mi opinión, es un poco injusto comparar Next.js y Gatsby con Remix, ya que Remix existe hace menos tiempo y pudo aprender de los problemas de sus predecesores. Con eso dicho, no cabe duda que los desarrolladores de Remix hicieron un gran trabajo en elaborar un framework que combine buena performance para el usuario final con una excelente experiencia de desarrollo. Si bien no utilizaría Remix para absolutamente todos los proyectos, es una herramienta valiosa a tener en cuenta al decidir sobre futuras arquitecturas y con eso entregar las soluciones más óptimas a nuestros clientes.

Eli Leonhardt
Eli Leonhardt

Software Engineer, team Atlas.