Cómo modularizó Netflix su código base JavaScript
Netflix implementó un plan para modularizar código JavaScript que permitió una unidad de pruebas de módulos individuales y actualizaciones diarias.
Hace varios años, Netflix decidió refactorizar su aplicación principal cliente de C++ a JavaScript. Este enfoque prometió una interfaz de usuario más consistente (UI) y permitió a Netflix innovar más rápidamente con nuevos servicios. Pero el planteamiento inicial dio lugar a una gran aplicación JavaScript frágil que era complicada para evaluar y actualizar.
En el último año, Netflix implementó un plan para modularizar código JavaScript que ayudó a hacer frente a muchas de estas limitaciones, dijo Semmy Purewal, ingeniero de software senior de Netflix, durante la Fluent Conference. Esto permitió una unidad de pruebas de módulos individuales, mejor código compartido entre equipos y habilitó a Netflix para impulsar actualizaciones diarias en lugar de cada seis semanas.
La arquitectura cliente de Netflix fue diseñada para imitar la flexibilidad de los navegadores web, permitiendo cambios a varios dispositivos. La empresa comenzó con un navegador especial que envolvió la solución WebKit alrededor del código del núcleo de soporte para la interfaz de usuario. Esto hizo que fuera más fácil para los ingenieros de Netflix actualizar la interfaz de usuario como cualquier aplicación en la Web.
Cambiando a JavaScript
Este enfoque ayudó a Netflix a crear una interfaz de usuario, pero no funcionó bien en los dispositivos más antiguos. La creación de aplicaciones que fueron consistentes a través de dispositivos era difícil porque había operaciones como la manipulación DOM que tuvieron que recorrer un árbol gigante para insertar elementos de la UI. Así que Netflix tenía que decidir si necesitaban todo el DOM y todo el equipaje que viene con él, dijo Purewal.
La forma en que trabajaron en torno a esta situación fue construir un motor de renderizado personalizado. Esta arquitectura funciona como Node.js, pero en el cliente en lugar del servidor, dijo Purewal. Este enfoque permite a la aplicación principal de rendering acceder a los recursos del sistema para la plataforma Netflix a través de los diversos dispositivos, desde plataformas de juego de alto desempeño hasta los televisores básicos.
Era necesario manejar las funciones relacionadas al sistema como la decodificación y reproducción de video, creación de redes, la generación de logs, la seguridad, el control de contenido y almacenamiento en caché. Este enfoque permite a Netflix innovar en todas las demás funciones y servicios prestados a través de esta plataforma común. Por ejemplo, se han probado nuevas formas de hacer que las películas empiecen más rápido, asegurar la información, enviar flujos adaptados y llevar los registros.
Problema de alcance de JavaScript
El problema era que gran parte de estas funciones habían existido en C++ y una vez que este código llegó a los dispositivos, no siempre fue actualizada en el campo. El objetivo era pasar a JavaScript más de las cosas de desempeño no crítico para permitir una mayor innovación. Implementaron la Capa de seguridad de mensajería Netflix, MSL JS, como un protocolo de mensajería segura flexible y extensible para la comunicación de datos.
Esto llevó a algunas diferencias interesantes en el alcance entre las variables en JavaScript y C++. Los problemas surgieron cuando dos variables supuestamente independientes también afectaron a una tercera variable que se puede acceder por ambos códigos. Estas variables supuestamente independientes terminaron afectando a las demás, a pesar de que no estaban destinadas a ser conectadas. Como resultado, el código sufrió mucho, dijo Purewal.
Los peligros del exceso de conexión
Como resultado de la naturaleza monolítica de esta base de código, Netflix no estaba haciendo pruebas de unidades. Dejó toda la prueba hasta el final con las pruebas de integración. Era difícil jactarse de que el nuevo código era dependiente. Netflix tampoco era capaz de compartir código entre los equipos. Cuando otros equipos de JavaScript necesitaran la misma funcionalidad, tendrían que traer todo el código que se requería.
Este es el mismo problema que puede ocurrir con programas orientados a objetos con una gran jerarquía de clases. Los desarrolladores tienen que incluir una base de código de gigante para llegar a una clase. Todos estos son anti-patrones relacionados con un amplio alcance, observó Purewal. El verdadero problema es que los desarrolladores no tenían acceso a las mismas características de lenguaje que existían en C++. Todos los otros lenguajes de programación modernos tienen una idea de mantener la independencia.
Hacer un JavaScript orientado a objetos
En el pasado, JavaScript carecía de lenguaje familiares primitivo, lo que ayudó a ocultar datos y funcionalidades, como las clases. Netflix se volvió hacia los módulos para hacer construcciones similares a las clases para limitar su alcance. En este caso, los programadores definen internamente los objetos, les dan una API y al final los exportan. La idea de los módulos se popularizó en Node.js, y es ampliamente disponible. ECMAScript 6 también tiene módulos integrados.
La programación modular es un superconjunto de programación orientada a objetos basada en clases. Es posible hacer que un módulo exporte una clase específica. La programación modular es también un subconjunto de la programación procedimental. Los procedimientos deben ser recogidos en términos de módulos. La ventaja de utilizar este tipo de programación es que los desarrolladores pueden hacer el desarrollo independiente de los sistemas. Son pequeños, concretos y se pueden poner en gestores de paquetes.
Este enfoque es compatible con dos de las buenas ideas de la programación orientada a objetos (OO) relacionadas con la programación por contrato y la programación de una interfaz, dijo Purewal. Esto hace que sea más fácil para un desarrollador cambiar un módulo sin afectar a los demás y permite el uso de gestores de paquetes como npm.
Transición gradual
Netflix rediseñó su código hacia módulos en el transcurso de un año. Ellos se mudaron de CMake a Grunt para hacer los artefactos de JavaScript. Browserify ayudó a Netflix a construir de manera eficiente paquetes de JavaScript. Jasmine simplificó el proceso de unidad de pruebas de nuevos módulos. El primer módulo fue el EventEmitter, que fue modelado más o menos después de la API Node.js.
Nuevas funciones se implementaron como módulos comunes de JavaScript. Netflix también tomó todos los antiguos subsistemas simples y los rediseñó como subsistemas basados en instancias en JavaScript. Se utilizó la inyección de dependencia cuando fue necesario compartir esos subsistemas en lugar de utilizar las propiedades globales.
Purewal aconseja a otras empresas hacer una transición similar a JavaScript utilizando herramientas de agrupamiento como Browserify o webpack. También es una buena idea empezar poco a poco con la exportación de la API de uno o dos módulos y luego establecer una política para que las nuevas características se implementen como módulos. "En su mayor parte, nuestro código evoluciona tan rápidamente que muchas de esas cosas se van, de todos modos", señaló Purewal.
Después de esta transición, Netflix se mudó de tres ciclos de la semana, a una implementación diaria. "Fue casi como un cambio de juego para nosotros", dijo Purewal. "Nuestro código era más delgado, más organizado y más comprobable”.