A very important part of the build phase of a single-page app it’s the bundling process, which consists in combining the app’s files and modules needed in order to run it.

In Mango’s Dashboard we used Browserify to structure the front-end.

Browserify provides lots of advantages and useful features as we outlined in this post.

The simplest bundling process using Browserify has an output of a single JavaScript file that contains the whole application logic.

The problem with single bundles

As new features are added to the app, the size of the bundle also increases and create some inconvenients:


– Download time increases.
– Time to interpret the code also increases.

Our Dashboard -like most single-page apps- needs to download, parse and interpret code before rendering the views.
This creates longer waiting periods before being able to use the app, directly affecting the user experience.

The first problem -although real- it does’t manifest in a linear fashion as the output code increases. As TJ VanToll points out, gzip compression is very powerful and works better with larger files (the bigger the file, the more repetitions it needs to find).

The bigger problem relies with the parsing and interpreting timeframe of the initial code.

Filament Group made an interesting analysis about the penalisation of using certain popular MVC frameworks that provides a clear vision of the problem.

factor-bundle to the rescue

factor-bundle works as a Browserify plugin that receives multiple files as entry points (in our case are mapped to the sections of our app), analyses their content and generates two things: an output file for each input file, and also an extra file that contains the related dependencies between them.

Thanks to this ‘factorization’ of dependencies, when users load the app, only downloads the JavaScript associated to the section that they’re visiting and the related dependencies. The remainder sections will only download in an asynchronic way and without repeating the dependencies downloaded while the users navigate the Dashboard.

A small optimization we make is for each output file creating another file with the shared dependencies. This avoids downloading an extra script (-common-.js).

The output after the process looks like this:

.
|-- config-0.3.6.js
|-- config-common-0.3.6.js
|-- login-0.3.6.js
|-- login-common-0.3.6.js
|-- store-single-0.3.6.js
|-- store-single-common-0.3.6.js
|-- stores-0.3.6.js
|-- stores-common-0.3.6.js
|-- user-0.3.6.js
`-- user-common-0.3.6.js

There are optimisations to delay downloading not used modules, i.e. detecting mouse proximity to links to different sections or similar strategies. We decided to talk about these optimisations in future posts.

Conclusions

Separating bundles resulted in an optimization of waiting times of around 30% when users logged in to our Dashboard; thanks to the decreased size of the initial script and also the fewer amount of code to interpret. Also, adding new sections doesn’t impact the performance of the app’s initial load.

There are a lot of techniques to enhance the initial load of single-page apps. One we’re investigating right now is the shared rendering between client and server. A very interesting advantage of bundle factorization is that it’s almost exclusively a change in the compilation process of the app.
Business logic and rules remained intact.

Any doubt, comment or experiences you want to share, please contact me via Twitter: @dzajdband.

Durante el pasaje a producción de una single-page application (SPA) es muy importante el proceso de bundling, que consiste en combinar los archivos y módulos de la aplicación para su puesta en marcha.

En el panel de Mango utilizamos Browserify para estructurar el Front-end.
Browserify nos proporciona muchas ventajas y utilidades como contamos en este post.
El proceso de bundling más simple utilizando browserify resulta en un único archivo de JavaScript que contiene toda la lógica de la aplicación.

El problema del bundle único

A medida que se agregan nuevas features el bundle resultante incrementa en peso acarreando dos inconvenientes:


– El tiempo de descarga es mayor.
– El tiempo de interpretado del código es mayor.

El panel, como la mayoría de las SPA, necesita descargar, parsear e interpretar el código antes de empezar a dibujar. Esto se traduce en tiempos más largos de espera antes de poder utilizar la aplicación, afectando a la experiencia del usuario.

El primer problema, si bien es real, no se manifiesta de modo lineal a medida que crece la cantidad de código resultante. Como apunta TJ VanToll la compresión gzip es muy fuerte y funciona mejor para archivos más grandes (mientras más grande el archivo hay más repeticiones por encontrar).

El inconveniente en materia de performance más grande está relacionado con el tiempo de parsing e interpretado del código inicial.

Filament group hizo un análisis interesante sobre la penalización en la utilización de ciertos frameworks MVC populares en materia de carga inicial que da un buen panorama del problema.

factor-bundle al rescate

factor-bundle funciona como un plugin de browserify que recibe múltiples archivos como puntos de entrada (en nuestro caso están mapeados a las secciones de nuestra aplicación), analiza sus contenidos y genera por un lado un archivo de salida por cada archivo de entrada sumado a un archivo extra que contiene las dependencias en común entre todos ellos.

Gracias a esta ‘factorización’ de dependencias, cuando un usuario entra a la app solo descarga el JavaScript asociado a la sección que visita, sumado a las dependencias compartidas. El resto de las secciones se descargan de modo asincrónico y sin repetir la descarga de dependencias, mientras el usuario navega por el panel.

Una pequeña optimización que hacemos es producir, por cada archivo de salida, otro que surge de concatenarle a este el archivo de dependencias comunes. Esto previene la descarga de un script adicional (-common-.js).

El output luego del procesado se visualiza de este modo:

.
|-- config-0.3.6.js
|-- config-common-0.3.6.js
|-- login-0.3.6.js
|-- login-common-0.3.6.js
|-- store-single-0.3.6.js
|-- store-single-common-0.3.6.js
|-- stores-0.3.6.js
|-- stores-common-0.3.6.js
|-- user-0.3.6.js
`-- user-common-0.3.6.js

Existen optimizaciones para aplazar la carga de módulos no usados. Por ejemplo detectar el acercamiento del mouse a links de otras secciones. Decidimos dejar esas optimizaciones para más adelante (y para otros posts).

Conclusiones

La técnica de separación de bundles resultó en una mejora de aproximadamente un 30% de ahorro en el tiempo de espera promedio de los usuarios a la hora de ingresar a nuestro Panel de administración, dado por la baja en el peso del script inicial como la menor cantidad de código a interpetar. Además, el agregado de nuevas secciones a la aplicación no resulta en una reducción de la performance en la carga inicial del sistema.

Hay otras técnicas para mejorar la carga inicial de SPAs. Una de las que estamos investigando para integrar es la implementación de rendering compartido entre el cliente y el servidor. Una ventaja interesante de la factorización de bundles es que es un cambio casi exclusivamente del proceso de compilación de la aplicación. La lógica de negocio permaneció intacta.

Cualquier duda/comentario o experiencias que quieran compartir, me pueden contactar vía Twitter a @dzajdband.

6to5-2

Next Saturday night, we’re sending you back to the future!

– Dr. Emmett L. Brown, Back to the Future (1985)

Like the nerds we are, we constantly check new technologies and the evolution of the ones we’re already interested in. That’s why the discussion of the implementation of different aspects of the EcmaScript standard keeps us interested and gives us the motivation to experiment new things.

Backwards compatibility is always important on the client-side JavaScript development process. That’s one reason why Can I use is one of the tools we use almost everyday. Looking forward is always exciting.
The problem with this is knowing which tools are going to be really useful from the ones that are just a fad.

After a JSConfAR filled with talks about ES6, I decided to investigate the possibility of adding a ES6 to ES5 transpiler and its potential benefits. Our aim included that Mango’s Dashboard browser compatibility included IE8+ and modern browsers.

6to5

The search was successful and led me to 6to5. The slogan says it all: «Next generation JavaScript, today». The idea is simple and powerful, a lot of the features of the new standards can be converted with the help of transformers, to a ES5 compatible syntax, making possible to run your code in older versions of Internet Explorer, for example. The 6to5 website has a REPL that allows you to experiment and easily become a fan of it.

The implementation of 6to5 to our build was done literally with 2 lines. The 6to5 also maintains 6to5ify, a Browserify plugin (something we talked about in a previous post), that analyses the ES6 compatible code and turns it into ES5. Something important to point out is that the implementation of 6to5 didn’t involve modifying not even one line of code from our product. The code doesn’t need to be modified at all.

Using the Browserify CLI, you can run 6to5ify in the following way:

$ browserify script.js -t 6to5ify --outfile bundle.js

In order to support some features on the browser, you should include polyfills already present in 6to5. In order to do that in our development environment, we only needed to add this: `require(‘6to5/polyfill’)`.

Better than ever

During the first week of using 6to5 we used a lot of ES6 features in our code daily.

Without going into detail, here are some of the ones we’re using right now:

Arrow functions

This construction is fundamental for a language and a platform (the web) that depends so much on callbacks. The arrow functions are not only a shortcut to define functions, but also have a very useful feature such as lexical binding. Thanks to arrow functions we can almost forget about the classic `self = this`. Look to the following example, of the first commit that uses ES6 in our code:

Before:

Tabs.prototype.show = function() {
  var self = this;
  // ...
  this.$triggers.on('click', function(eve) {
    self.select(target.getAttribute('data-tabs-trigger'));
    });
  }

After:

  Tabs.prototype.show = function() {
    // ...
    this.$triggers.on('click', (eve) => this.select(target.getAttribute('data-tabs-trigger'));
  }

 

Classes

In our modules, it’s very common to find the following pattern:

function Input(options) { // code }
inherit(Input, Emitter); // Input prototype inherits from Emitter
Input.prototype.val = function() {} // Extend prototype

The classes syntax in ES6 manifest this patter much more clearly:

class Input extends Emitter {
  constructor() { //code }
  val() { //... }
}

 

let + const

As its said `let` is the new `var`, but with Block scoping. It’s interesting that in exception of the cases where the block scope became relevant the transpiler converts `let` statements into var’s.

let a = 3; // written code
var a = 3; // transpiled code (in most contexts)

// written code
let x = 2;
{
  let x = 3; // new var different than above
}

// Transpiled code
var x = 2;
{
  var _x = 3;
}

Regarding adding constants using the keyword `const`, I remember a colleague that worked with Java who used to ask me if I didn’t like to declare constants or if there was a problem with the language design. This new keyword implies for 6to5 making checks at compilation time. If the static code analysis detects a double assign, it simply stops the compilation process and prompts the error. One of the uses that we’re assigning to `const` besides declaring new constants, is using it for the `require`. We don’t want to modify the objects that export other modules directly. Using `const`, we make sure we didn’t make a mistake.

const Input = require('input');

 

Parameter destructuring

Another pattern in our code is a constructor that receives a series of options via a parameter that is an object in the following way:

function Form(options) {
  options = options || {};
  this.size = options.size;
  this.legend = options.legend;
}

using ES6 we could write:

function Form({size, legend}={}) {
  this.size = size;
  this.legend = legend;
}

 

Template Strings

Anyone who hasn’t created a URL in the following way, throw the first stone.

function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

Template strings make this more friendly:

// Written code
function fetchData(id) {
  request('/api/controller/${id}/action/');
}

// Transpiled code
function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

 

Conclusions

The use of the features above and some others, helps us to be more productive and also to write more friendly code and less error-prone. Making the decision to code this way is not just about the comfort of the developers, but also impacts on productivity for projects where there’s need to maintain code already in production.

We hope to continue discovering interesting paths using ES6 and letting you know about how it can help us to enhance the quality of the web development.

 

ES7: Looking beyond the horizon

Once ES6 was completed, the discussion about ES7 started. Just like 6to5 allows us to use ES6 features before it’s available in some environments, the same happens with ES7.

That’s why we are in conditions to make use of such keywords as `async` and `await`that allows async programming a lot like sync, or event use generator comprehensions. The latter is available as an `experimental feature` for 6to5.

More resources:

ES6 Rocks
Understanding ES6
Axel Rauschmayer Blog
6to5 Tour
ES6 Overview

6to5-2

«El futuro llegó hace rato todo un palo, ya lo ves.»

Carlos Solari – Todo un palo

Como buenos nerds estamos constantemente detrás del desarrollo de nuevas tecnologías y de la evolución de aquellas que nos interesan. Es por eso que la discusión e implementación de aspectos del estándar de EcmaScript nos encuentra siempre atentos y con ganas de experimentar.

Por lo general en la vida del JavaScript en el cliente se suele mirar todo el tiempo la compatibilidad para atras. Así es como Can I use es frecuentado diariamente por nosotros. Mirar para adelante es motivo de exaltación.
El problema principal está en separar las nuevas herramientas que resultan útiles de aquellas que se nos aparecen simplemente por una cuestión de moda o marketing.

Luego de una JSConfAR plagada de charlas sobre ES6 decidí investigar la viabilidad técnica y los potenciales beneficios de agregar un transpiler de ES6 a ES5 (el compromiso de compatibilidad del panel de administración de Mango es de IE8+ y navegadores modernos).

6to5

La búsqueda fue fructífera y me llevó a 6to5. El slogan es claro: «Next generation JavaScript, today». La idea es simple y poderosa, muchas de las características de los nuevos estándares pueden convertirse con la ayuda de transformaciones a una sintaxis compatible con ES5, posibilitando correr el código en por ejemplo versiones antiguas de Internet Explorer. El sitio de 6to5 cuenta con un REPL que permite jugar y fanatizarse rápidamente.

La inclusión de 6to5 a nuestro build system fue literalmente de 2 lineas. El equipo de 6to5 también mantiene 6to5ify, un plugin de Browserify, del cual hablamos en un post anterior, que analiza el código ES6 compatible y lo convierte a ES5. Un punto a destacar es que la inclusión de 6to5 no involucró la modificación de ni una linea de código del producto. El código escrito no necesita ser modificado (no hay excusas).

Usando el CLI de Browserify, se puede correr 6to5ify del siguiente modo:

$ browserify script.js -t 6to5ify --outfile bundle.js

Para soportar algunas características en el navegador se necesitan incluir polyfills que vienen incluidos en 6to5. La inclusión de los polyfills estuvo a un `require(‘6to5/polyfill’)` de nuestros ambiente de desarrollo.

Ya lo estamos usando y está más lindo que nunca

Durante la primera semana de uso de 6to5 en el día a día usamos muchas features de ES6 en nuestro código.

Sin explicar sus funciones detalladamente, paso a detallar el uso que le estamos dando a algunas de ellas:

Arrow functions

Esta construcción es fundamental para un lenguaje y una plataforma (la web) que delega tanto peso en los callbacks. Las arrow functions no son solo un shortcut para definir funciones sino que tienen una característica super útil como es el lexical binding. Gracias a las arrow functions nos olvidamos casi por completo del clásico `self = this`. Este ejemplo es del primer commit que usa ES6 en nuestro código.

Antes:

Tabs.prototype.show = function() {
  var self = this;
  // ...
  this.$triggers.on('click', function(eve) {
    self.select(target.getAttribute('data-tabs-trigger'));
    });
  }

Después:

  Tabs.prototype.show = function() {
    // ...
    this.$triggers.on('click', (eve) => this.select(target.getAttribute('data-tabs-trigger'));
  }

 

Classes

En nuestros módulos es muy común encontrar el siguiente patrón

function Input(options) { // code }
inherit(Input, Emitter); // Input prototype inherits from Emitter
Input.prototype.val = function() {} // Extend prototype

La sintaxis de clases de ES6 manifiesta este patrón de un modo claro

class Input extends Emitter {
  constructor() { //code }
  val() { //... }
}

 

let + const

Como se suele decir `let` es el nuevo `var`, pero con Block scoping. Es interesante que salvo los casos donde el scope de bloque cobra valor, el transpiler pasa las declaraciones de `let` directamente a var.

let a = 3; // written code
var a = 3; // transpiled code (in most contexts)

// written code
let x = 2;
{
  let x = 3; // new var different than above
}

// Transpiled code
var x = 2;
{
  var _x = 3;
}

Respecto al agregado de constantes mediante la keyword `const` recuerdo a un compañero de trabajo que venía de Java y me preguntaba si no me gustaba declarar constantes o si era una falla de diseño del lenguaje. Esta nueva keyword implica para 6to5 chequeos en tiempo de compilación. Si el análisis de código estático detecta una doble asignación, simplemente frena la compilación y nos advierte del error. Un uso que le estamos dando a `const` más allá del de declarar nuevas constantes es usarlo para los `require`. No queremos modificar los objetos que exportan otros módulos directamente. Usando `const` nos aseguramos de no equivocarnos.

const Input = require('input');

 

Parameter destructuring

Otro patrón común en nuestro código es un constructor que recibe una serie de opciones mediante un parámetro que es un objeto del siguiente modo:

function Form(options) {
  options = options || {};
  this.size = options.size;
  this.legend = options.legend;
}

usando ES6 podemos escribir:

function Form({size, legend}={}) {
  this.size = size;
  this.legend = legend;
}

 

Template Strings

El que nunca armó una url de esta manera que tire la primer piedra:

function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

Los template strings lo hacen amigable:

// Written code
function fetchData(id) {
  request('/api/controller/${id}/action/');
}

// Transpiled code
function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

 

Conclusiones

El uso de las features detalladas y algunas otras nos ayudan a ser más productivos y a la vez escribir código más entendible y menos propenso a errores. Decisiones de este tipo no solo hacen a la comodidad del programador sino que se traducen en mejoras de productividad para proyectos y empresas tanto a la hora de generar nuevas oportunidades como de mantener código previamente puesto en producción.

Esperamos seguir descubriendo caminos interesantes usando ES6 y contar como nos puede ayudar a mejorar la calidad del desarrollo web.

 

ES7: Mirando un poco más allá del horizonte

Una vez completado ES6 se empezó a debatir ES7. Así como 6to5 nos permite usar funcionalidades de ES6 antes de estar disponible en ciertos ambientes, lo mismo ocurre con ES7.

Es por eso que estamos en condiciones de hacer uso de las keywords async/await que hacen sentir la programación asíncrona como si estuviéramos escribiendo código sync o generator comprehensions que justamente permiten construir generadores por comprensión. Esta última se encuentra en modo experimental para 6to5.

Algunos recursos interesantes:

ES6 Rocks
Understanding ES6
Blog de Axel Rauschmayer
Tour de 6to5
ES6 Overview