Medición del impacto en el rendimiento real de los trabajadores de servicios

Uno de los beneficios más significativos de los servicios de trabajo (al menos desde una perspectiva de rendimiento) es su capacidad para controlar de forma proactiva el almacenamiento en caché de los recursos. Una aplicación web que puede almacenar en caché todos sus recursos necesarios debería cargarse mucho más rápido para los visitantes recurrentes. Pero, ¿cómo se ven estas mejoras para los usuarios reales? ¿Y cómo se mide esto?

La app web de Google I/O (IOWA, en resumen) es una app web progresiva que aprovecha la mayoría de las nuevas funciones que ofrecen los trabajadores del servicio para ofrecer a sus usuarios una experiencia enriquecida similar a una app. También usó Google Analytics para captar datos de rendimiento y patrones de uso clave de su público de usuarios amplio y diverso.

En este caso de éxito, se explora cómo IOWA usó Google Analytics para responder preguntas clave de rendimiento y generar informes sobre el impacto real de los trabajadores del servicio.

Comienza con las preguntas

Cada vez que implementes estadísticas en un sitio web o una aplicación, es importante comenzar por identificar las preguntas que intentas responder a partir de los datos que recopilarás.

Si bien teníamos varias preguntas que queríamos responder, para los fines de este caso de éxito, centrémonos en dos de las más interesantes.

1. ¿La caché del trabajador del servicio tiene un mejor rendimiento que los mecanismos de almacenamiento en caché HTTP existentes disponibles en todos los navegadores?

Ya esperamos que las páginas se carguen más rápido para los visitantes recurrentes que para los nuevos, ya que los navegadores pueden almacenar en caché las solicitudes y entregarlas al instante en las visitas repetidas.

Los Service Workers ofrecen capacidades de almacenamiento en caché alternativas que les brindan a los desarrolladores un control detallado sobre qué se almacena en caché y cómo se hace. En IOWA, optimizamos nuestra implementación de service worker para que cada recurso se almacenara en caché, de modo que los visitantes recurrentes pudieran usar la app completamente sin conexión.

Pero, ¿este esfuerzo sería mejor que lo que el navegador ya hace de forma predeterminada? Si es así, ¿cuánto mejor? 1

2. ¿Cómo afecta el trabajador de servicio a la experiencia de carga del sitio?

En otras palabras, ¿qué tan rápido siente que se carga el sitio, independientemente de los tiempos de carga reales que miden las métricas de carga de página tradicionales?

Obviamente, responder preguntas sobre cómo se siente una experiencia no es una tarea fácil, y ninguna métrica representará perfectamente un sentimiento tan subjetivo. Dicho esto, sin duda hay algunas métricas que son mejores que otras, por lo que es importante elegir las correctas.

Elige la métrica correcta

De forma predeterminada, Google Analytics realiza un seguimiento de los tiempos de carga de la página (a través de la API de Navigation Timing) para el 1% de los visitantes de un sitio y pone esos datos a disposición a través de métricas como el Tiempo de carga promedio de la página.

El tiempo de carga promedio de la página es una buena métrica para responder nuestra primera pregunta, pero no es una métrica particularmente buena para responder la segunda. En primer lugar, el evento load no corresponde necesariamente al momento en que el usuario puede interactuar con la app. Además, dos apps con el mismo tiempo de carga exacto pueden dar la sensación de que se cargan de manera muy diferente. Por ejemplo, un sitio con una pantalla de presentación o un indicador de carga probablemente parezca que se carga mucho más rápido que un sitio que solo muestra una página en blanco durante varios segundos.

En IOWA, mostramos una animación de cuenta regresiva en la pantalla de presentación que (en mi opinión) sirvió con mucho éxito para entretener al usuario mientras se cargaba el resto de la app en segundo plano. Por lo tanto, hacer un seguimiento del tiempo que tarda en aparecer la pantalla de presentación tiene mucho más sentido como una forma de medir el rendimiento de carga percibido. Elegimos la métrica tiempo para la primera pintura para obtener este valor.

Una vez que decidimos las preguntas que queríamos responder y identificamos las métricas que serían útiles para responderlas, llegó el momento de implementar Google Analytics y comenzar a realizar mediciones.

La implementación de estadísticas

Si ya usaste Google Analytics, es probable que conozcas el fragmento de seguimiento de JavaScript recomendado. El aspecto resultante será el siguiente:

<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>

La primera línea del código anterior inicializa una función ga() global (si aún no existe) y la última línea descarga de forma asíncrona la biblioteca analytics.js.

La parte central contiene estas dos líneas:

ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');

Estos dos comandos realizan un seguimiento de las páginas que visitan las personas que llegan a tu sitio, pero no mucho más. Si quieres hacer un seguimiento de interacciones adicionales del usuario, debes hacerlo por tu cuenta.

En el caso de IOWA, queríamos hacer un seguimiento de dos aspectos adicionales:

  • Es el tiempo transcurrido entre el momento en que la página comienza a cargarse y el momento en que aparecen los píxeles en la pantalla.
  • Indica si un service worker controla la página o no. Con esta información, podríamos segmentar nuestros informes para comparar los resultados con y sin el trabajador de servicio.

Captura del tiempo hasta la primera pintura

Algunos navegadores registran la hora precisa en la que se pinta el primer píxel en la pantalla y ponen ese tiempo a disposición de los desarrolladores. Ese valor, en comparación con el valor de navigationStart expuesto a través de la API de Navigation Timing, nos brinda un registro muy preciso de cuánto tiempo transcurrió entre el momento en que el usuario solicitó la página inicialmente y el momento en que vio algo por primera vez.

Como ya mencioné, el tiempo hasta la primera pintura es una métrica importante que se debe medir porque es el primer punto en el que un usuario experimenta la velocidad de carga de tu sitio. Es la primera impresión que tienen los usuarios, y una buena primera impresión puede afectar de forma positiva el resto de la experiencia del usuario.2

Para obtener el primer valor de pintura en los navegadores que lo exponen, creamos la función de utilidad getTimeToFirstPaintIfSupported:

function getTimeToFirstPaintIfSupported() {
  // Ignores browsers that don't support the Performance Timing API.
  if (window.performance && window.performance.timing) {
    var navTiming = window.performance.timing;
    var navStart = navTiming.navigationStart;
    var fpTime;

    // If chrome, get first paint time from `chrome.loadTimes`.
    if (window.chrome && window.chrome.loadTimes) {
      fpTime = window.chrome.loadTimes().firstPaintTime * 1000;
    }
    // If IE/Edge, use the prefixed `msFirstPaint` property.
    // See http://msdn.microsoft.com/ff974719
    else if (navTiming.msFirstPaint) {
      fpTime = navTiming.msFirstPaint;
    }

    if (fpTime && navStart) {
      return fpTime - navStart;
    }
  }
}

Con esto, ahora podríamos escribir otra función que envíe un evento de no interacción con el tiempo de primera pintura como valor:3

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    ga('send', 'event', {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    });
  }
}

Después de escribir ambas funciones, nuestro código de seguimiento se ve de la siguiente manera:

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sends a pageview for the initial pageload.
ga('send', 'pageview');

// Sends an event with the time to first paint data.
sendTimeToFirstPaint();

Ten en cuenta que, según el momento en que se ejecute el código anterior, es posible que los píxeles ya se hayan pintado en la pantalla o no. Para asegurarnos de ejecutar este código siempre después de que se produce la primera pintura, pospusimos la llamada a sendTimeToFirstPaint() hasta después del evento load. De hecho, decidimos posponer el envío de todos los datos de estadísticas hasta después de que se cargue la página para garantizar que esas solicitudes no compitan con la carga de otros recursos.

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

El código anterior informa firstpaint veces a Google Analytics, pero eso es solo la mitad de la historia. Aún necesitábamos hacer un seguimiento del estado del service worker; de lo contrario, no podríamos comparar los primeros tiempos de pintura de una página controlada por un service worker y una página no controlada.

Determina el estado del service worker

Para determinar el estado actual del trabajador de servicio, creamos una función de utilidad que muestra uno de los siguientes tres valores:

  • controlada: Un service worker controla la página. En el caso de IOWA, eso también significa que todos los recursos se almacenaron en caché y que la página funciona sin conexión.
  • compatible: El navegador admite el service worker, pero este aún no controla la página. Este es el estado esperado para los visitantes nuevos.
  • No se admite: El navegador del usuario no admite el trabajador de servicio.
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

Esta función obtuvo el estado del trabajador del servicio por nosotros. El siguiente paso fue asociar este estado con los datos que enviamos a Google Analytics.

Realiza un seguimiento de los datos personalizados con dimensiones personalizadas

De forma predeterminada, Google Analytics te ofrece muchas formas de subdividir tu tráfico total en grupos según los atributos del usuario, la sesión o la interacción. Estos atributos se conocen como dimensiones. Entre las dimensiones comunes que les interesan a los desarrolladores web, se encuentran Navegador, Sistema operativo o Categoría de dispositivo.

El estado del trabajador del servicio no es una dimensión que Google Analytics proporcione de forma predeterminada. Sin embargo, Google Analytics te permite crear tus propias dimensiones personalizadas y definirlas como desees.

Para IOWA, creamos una dimensión personalizada llamada Estado del trabajador de servicio y configuramos su alcance en hit (es decir, por interacción).4 Cada dimensión personalizada que creas en Google Analytics tiene un índice único dentro de esa propiedad y, en tu código de seguimiento, puedes hacer referencia a esa dimensión por su índice. Por ejemplo, si el índice de la dimensión que acabamos de crear fuera 1, podríamos actualizar nuestra lógica de la siguiente manera para enviar el evento firstpaint y, de esta forma, incluir el estado del trabajador del servicio:

ga('send', 'event', {
  eventCategory: 'Performance',
  eventAction: 'firstpaint',
  // Rounds to the nearest millisecond since
  // event values in Google Analytics must be integers.
  eventValue: Math.round(timeToFirstPaint)
  // Sends this as a non-interaction event,
  // so it doesn't affect bounce rate.
  nonInteraction: true,

  // Sets the current service worker status as the value of
  // `dimension1` for this event.
  dimension1: getServiceWorkerStatus()
});

Esto funciona, pero solo asociará el estado del trabajador de servicio con este evento en particular. Dado que el Estado del trabajador de servicio es algo que puede ser útil conocer para cualquier interacción, es mejor incluirlo con todos los datos que se envían a Google Analytics.

Para incluir esta información en todos los hits (p.ej., todas las vistas de página, los eventos, etc.), establecemos el valor de la dimensión personalizada en el objeto tracker, antes de enviar datos a Google Analytics.

ga('set', 'dimension1', getServiceWorkerStatus());

Una vez establecido, este valor se envía con todos los hits posteriores de la carga de página actual. Si el usuario vuelve a cargar la página más adelante, es probable que se muestre un valor nuevo de la función getServiceWorkerStatus(), y ese valor se establecerá en el objeto del servicio de seguimiento.

Nota breve sobre la claridad y la legibilidad del código: Dado que es posible que otras personas que vean este código no sepan a qué se refiere dimension1, siempre es mejor crear una variable que asigne nombres de dimensiones significativos a los valores que usará analytics.js.

// Creates a map between custom dimension names and their index.
// This is particularly useful if you define lots of custom dimensions.
var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1'
};

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sets the service worker status on the tracker,
// so its value is included in all future hits.
ga('set', customDimensions.SERVICE_WORKER_STATUS, getServiceWorkerStatus());

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

Como mencioné, enviar la dimensión Estado del trabajador de servicio con cada hit nos permite usarla cuando se informa sobre cualquier métrica.

Como puedes ver, casi el 85% de todas las vistas de página de IOWA provinieron de navegadores que admiten service worker.

Los resultados: responder nuestras preguntas

Una vez que comenzamos a recopilar datos para responder nuestras preguntas, pudimos generar informes sobre esos datos para ver los resultados. (Nota: Todos los datos de Google Analytics que se muestran aquí representan el tráfico web real al sitio de IOWA del 16 al 22 de mayo de 2016).

La primera pregunta que nos hicimos fue: ¿La caché de service worker tiene un mejor rendimiento que los mecanismos de almacenamiento en caché HTTP existentes disponibles en todos los navegadores?

Para responder esa pregunta, creamos un informe personalizado en el que analizamos la métrica Tiempo de carga promedio de la página en varias dimensiones. Esta métrica es adecuada para responder esta pregunta porque el evento load se activa solo después de que se descargan todos los recursos iniciales. Por lo tanto, refleja directamente el tiempo de carga total de todos los recursos críticos del sitio.5

Las dimensiones que elegimos fueron las siguientes:

  • Nuestra dimensión personalizada Estado de service worker
  • Tipo de usuario, que indica si es la primera visita del usuario al sitio o si es un usuario recurrente. (Nota: Un visitante nuevo no tendrá ningún recurso almacenado en caché, pero un visitante recurrente sí).
  • Categoría del dispositivo, que nos permite comparar los resultados entre dispositivos móviles y computadoras.

Para controlar la posibilidad de que factores no relacionados con el service worker estuvieran sesgando nuestros resultados de tiempo de carga, limitamos nuestra consulta para que solo incluya navegadores que admitan service worker.

Como puedes ver, las visitas a nuestra app cuando están controladas por un service worker se cargaron bastante más rápido que las visitas no controladas, incluso las de usuarios recurrentes que probablemente tenían la mayoría de los recursos de la página almacenados en caché. También es interesante observar que, en promedio, los visitantes en dispositivos móviles con un trabajador de servicio vieron cargas más rápidas que los visitantes nuevos en computadoras de escritorio.

"…las visitas a nuestra app cuando están controladas por un service worker se cargan bastante más rápido que las visitas no controladas…"

Puedes ver más detalles en las siguientes dos tablas:

Tiempo de carga promedio de la página (computadoras de escritorio)
Estado del service worker Tipo de usuario Tiempo de carga de la página promedio (ms) Tamaño de la muestra
Controlaste Visitante recurrente 2568 30860
Compatible Visitante recurrente 3612 1289
Compatible Visitante nuevo 4664 21991
Tiempo de carga promedio de la página (dispositivos móviles)
Estado del service worker Tipo de usuario Tiempo de carga de la página promedio (ms) Tamaño de la muestra
Controlaste Visitante recurrente 3760 8162
Compatible Visitante recurrente 4843 676
Compatible Visitante nuevo 6158 5779

Es posible que te preguntes cómo es posible que un visitante recurrente cuyo navegador admita el servicio de trabajador esté en un estado no controlado. Existen varias explicaciones posibles:

  • El usuario abandonó la página en la visita inicial antes de que el service worker pudiera terminar de inicializarse.
  • El usuario desinstaló el trabajador de servicio a través de las herramientas para desarrolladores.

Ambas situaciones son relativamente raras. Podemos ver eso en los datos si observamos los valores de Page Load Sample en la cuarta columna. Observa que las filas del medio tienen una muestra mucho más pequeña que las otras dos.

Nuestra segunda pregunta fue: ¿Cómo afecta el trabajador de servicio a la experiencia de carga del sitio?

Para responder esta pregunta, creamos otro informe personalizado para la métrica Promedio de valor del evento y filtramos los resultados para que solo incluyan nuestros eventos firstpaint. Usamos las dimensiones Categoría del dispositivo y nuestra dimensión personalizada Estado del trabajador de servicio.

Al contrario de lo que esperaba, el servicio de trabajo en dispositivos móviles tuvo mucho menos impacto en el tiempo de primera pintura que en la carga general de la página.

“…el trabajador de servicio en dispositivos móviles tuvo mucho menos impacto en el tiempo de primera pintura que en la carga general de la página”.

Para explorar por qué es así, debemos profundizar en los datos. Los promedios pueden ser buenos para obtener resúmenes generales y descripciones generales, pero para tener una idea real de cómo se desglosan estas cifras en un rango de usuarios, debemos observar una distribución de firstpaint veces.

Cómo obtener la distribución de una métrica en Google Analytics

Para obtener la distribución de las firstpaint veces, necesitamos acceso a los resultados individuales de cada evento. Lamentablemente, Google Analytics no facilita esta tarea.

Google Analytics nos permite desglosar un informe por cualquier dimensión que deseemos, pero no nos permite desglosarlo por métricas. Eso no quiere decir que sea imposible, solo significa que tuvimos que personalizar nuestra implementación un poco más para obtener el resultado deseado.

Dado que los resultados de los informes solo se pueden desglosar por dimensiones, tuvimos que establecer el valor de la métrica (en este caso, la hora de firstpaint) como una dimensión personalizada en el evento. Para ello, creamos otra dimensión personalizada llamada Valor de métrica y actualizamos nuestra lógica de seguimiento de firstpaint de la siguiente manera:

var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1',
  <strong>METRIC_VALUE: 'dimension2'</strong>
};

// ...

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    var fields = {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    }

    <strong>// Sets the event value as a dimension to allow for breaking down the
    // results by individual metric values at reporting time.
    fields[customDimensions.METRIC_VALUE] = String(fields.eventValue);</strong>

    ga('send', 'event', fields);
  }
}

Actualmente, la interfaz web de Google Analytics no proporciona una forma de visualizar la distribución de valores de métricas arbitrarias, pero con la ayuda de la API de Google Analytics Core Reporting y la biblioteca de Google Charts, podemos consultar los resultados sin procesar y, luego, crear un histograma por nuestra cuenta.

Por ejemplo, se usó la siguiente configuración de solicitud de API para obtener una distribución de valores de firstpaint en computadoras de escritorio con un trabajador de servicio no controlado.

{
  dateRanges: [{startDate: '2016-05-16', endDate: '2016-05-22'}],
  metrics: [{expression: 'ga:totalEvents'}],
  dimensions: [{name: 'ga:dimension2'}],
  dimensionFilterClauses: [
    {
      operator: 'AND',
      filters: [
        {
          dimensionName: 'ga:eventAction',
          operator: 'EXACT',
          expressions: ['firstpaint']
        },
        {
          dimensionName: 'ga:dimension1',
          operator: 'EXACT',
          expressions: ['supported']
        },
        {
          dimensionName: 'ga:deviceCategory',
          operator: 'EXACT',
          expressions: ['desktop']
        }
      ],
    }
  ],
  orderBys: [
    {
      fieldName: 'ga:dimension2',
      orderType: 'DIMENSION_AS_INTEGER'
    }
  ]
}

Esta solicitud a la API muestra un array de valores que se ve de la siguiente manera (Nota: estos son solo los primeros cinco resultados). Los resultados se ordenan de menor a mayor, por lo que estas filas representan los tiempos más rápidos.

Resultados de la respuesta de la API (primeras cinco filas)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

A continuación, te indicamos lo que significan estos resultados en lenguaje sencillo:

  • Hubo 3 eventos en los que el valor de firstpaint fue de 4 ms.
  • Hubo 2 eventos en los que el valor de firstpaint fue de 5 ms.
  • Hubo 10 eventos en los que el valor de firstpaint fue de 6 ms.
  • Hubo 8 eventos en los que el valor de firstpaint fue de 7 ms.
  • Hubo 10 eventos en los que el firstpaint value fue de 8 ms.
  • etc.

A partir de estos resultados, podemos extrapolar el valor de firstpaint para cada evento y crear un histograma de la distribución. Hicimos esto para cada una de las consultas que ejecutamos.

Así se veía la distribución en computadoras de escritorio con un service worker no controlado (pero compatible):

Tiempo para la primera distribución de pintura en computadoras (compatible)

El tiempo firstpaint medio para la distribución anterior es de 912 ms.

La forma de esta curva es bastante típica de las distribuciones de tiempo de carga. Compara esto con el histograma que aparece a continuación, que muestra la distribución de los eventos de primera pintura para las visitas en las que un trabajador de servicio controlaba la página.

Tiempo para la primera distribución de pintura en computadoras de escritorio (controlado)

Ten en cuenta que, cuando un service worker controlaba la página, muchos visitantes experimentaron una primera pintura casi inmediata, con una mediana de 583 ms.

“…cuando un service worker controlaba la página, muchos visitantes experimentaron una primera pintura casi inmediata…”

Para tener una mejor idea de cómo se comparan estas dos distribuciones, en el siguiente gráfico, se muestra una vista combinada de ambas. El histograma que muestra las visitas de los trabajadores del servicio no controladas se superpone sobre el histograma que muestra las visitas controladas, y ambos se superponen sobre un histograma que muestra ambas combinadas.

Tiempo de distribución del primer procesamiento de imagen en computadoras

Algo que me pareció interesante de estos resultados fue que la distribución con un service worker controlado aún tenía una curva en forma de campana después del aumento inicial. Esperaba un gran aumento inicial y, luego, una disminución gradual, pero no esperaba un segundo pico en la curva.

Cuando investigué qué podría estar causando esto, descubrí que, aunque un service worker puede controlar una página, su subproceso puede estar inactivo. El navegador hace esto para ahorrar recursos. Obviamente, no necesitas que todos los service workers de todos los sitios que visitaste estén activos y listos en cualquier momento. Esto explica la cola de la distribución. Algunos usuarios experimentaron una demora mientras se iniciaba el subproceso del service worker.

Sin embargo, como puedes ver en la distribución, incluso con esta demora inicial, los navegadores con el trabajador de servicio entregaron contenido más rápido que los navegadores que pasan por la red.

Así se veía en dispositivos móviles:

Distribución del tiempo hasta el primer procesamiento de imagen en dispositivos móviles

Si bien aún teníamos un aumento considerable en los tiempos de primera pintura casi inmediatos, la cola era bastante más grande y larga. Es probable que esto se deba a que, en dispositivos móviles, iniciar un subproceso de trabajador de servicio inactivo lleva más tiempo que en computadoras. También explica por qué la diferencia entre el tiempo promedio de firstpaint no fue tan grande como esperaba (se analizó anteriormente).

“…en dispositivos móviles, iniciar un subproceso de service worker inactivo tarda más que en computadoras”.

A continuación, se muestra el desglose de estas variaciones de los tiempos medios de primera pintura en dispositivos móviles y computadoras de escritorio agrupados por estado del trabajador de servicio:

Tiempo promedio hasta el primer procesamiento de imagen (ms)
Estado del service worker Computadora de escritorio Dispositivos móviles
Controlaste 583 1634
Compatible (no controlado) 912 1933

Si bien la creación de estas visualizaciones de distribución requirió un poco más de tiempo y esfuerzo que crear un informe personalizado en Google Analytics, nos brindan una idea mucho mejor de cómo los trabajadores del servicio afectan el rendimiento de nuestro sitio que los promedios por sí solos.

Otro impacto de los service workers

Además del impacto en el rendimiento, los trabajadores del servicio también afectan la experiencia del usuario de otras maneras que se pueden medir con Google Analytics.

Acceso sin conexión

Los trabajadores del servicio permiten que los usuarios interactúen con tu sitio sin conexión. Si bien es probable que algún tipo de compatibilidad sin conexión sea fundamental para cualquier app web progresiva, determinar qué tan importante es en tu caso depende en gran medida de la cantidad de uso que se produce sin conexión. Pero ¿cómo lo medimos?

Para enviar datos a Google Analytics, se requiere una conexión a Internet, pero no es necesario que los datos se envíen en el momento exacto en que se produjo la interacción. Google Analytics admite el envío de datos de interacción después del hecho si especificas un desfase de tiempo (a través del parámetro qt).

Durante los últimos dos años, IOWA ha estado usando una secuencia de comandos de trabajador de servicio que detecta los hits fallidos de Google Analytics cuando el usuario no tiene conexión y los vuelve a reproducir más tarde con el parámetro qt.

Para hacer un seguimiento de si el usuario estaba en línea o sin conexión, creamos una dimensión personalizada llamada En línea y la configuramos con el valor de navigator.onLine. Luego, escuchamos los eventos online y offline, y actualizamos la dimensión según corresponda.

Para tener una idea de qué tan común era que un usuario estuviera sin conexión mientras usaba IOWA, creamos un segmento que se segmentaba para los usuarios con al menos una interacción sin conexión. Resulta que fue casi el 5% de los usuarios.

Notificaciones push

Los service workers permiten que los usuarios acepten recibir notificaciones push. En IOWA, los usuarios recibían una notificación cuando estaba por comenzar una sesión de su programa.

Al igual que con cualquier forma de notificación, es importante encontrar el equilibrio entre proporcionar valor al usuario y molestarlo. Para comprender mejor lo que sucede, es importante hacer un seguimiento de si los usuarios aceptan recibir estas notificaciones, si interactúan con ellas cuando llegan y si los usuarios que anteriormente aceptaron cambiaron su preferencia y las inhabilitaron.

En IOWA, solo enviamos notificaciones relacionadas con la programación personalizada del usuario, algo que solo podían crear los usuarios que accedían a sus cuentas. Esto limitaba el conjunto de usuarios que podían recibir notificaciones a los usuarios que habían accedido a sus cuentas (se realiza un seguimiento a través de una dimensión personalizada llamada Signed In) cuyos navegadores admitían notificaciones push (se realiza un seguimiento a través de otra dimensión personalizada llamada Notification Permission).

El siguiente informe se basa en la métrica Usuarios y nuestra dimensión personalizada Permiso de notificaciones, segmentada por usuarios que accedieron en algún momento y cuyos navegadores admiten notificaciones push.

Es genial ver que más de la mitad de nuestros usuarios que accedieron a su cuenta optaron por recibir notificaciones push.

Banners de instalación de aplicación

Si una app web progresiva cumple con los criterios y un usuario la usa con frecuencia, es posible que se le muestre un banner de instalación de la app para que la agregue a su pantalla principal.

En IOWA, realizamos un seguimiento de la frecuencia con la que se mostraban estas instrucciones al usuario (y si se aceptaban) con el siguiente código:

window.addEventListener('beforeinstallprompt', function(event) {
  // Tracks that the user saw a prompt.
  ga('send', 'event', {
    eventCategory: 'installprompt',
    eventAction: 'fired'
  });

  event.userChoice.then(function(choiceResult) {
    // Tracks the users choice.
    ga('send', 'event', {
      eventCategory: 'installprompt',
      // `choiceResult.outcome` will be 'accepted' or 'dismissed'.
      eventAction: choiceResult.outcome,
      // `choiceResult.platform` will be 'web' or 'android' if the prompt was
      // accepted, or '' if the prompt was dismissed.
      eventLabel: choiceResult.platform
    });
  });
});

De los usuarios que vieron un banner de instalación de la app, alrededor del 10% eligió agregarlo a su pantalla principal.

Posibles mejoras en el seguimiento (para la próxima vez)

Los datos de estadísticas que recopilamos de IOWA este año fueron invaluables. Sin embargo, la retrospectiva siempre revela carencias y oportunidades para mejorar la próxima vez. Después de terminar el análisis de este año, estas son dos cosas que me gustaría haber hecho de manera diferente y que los lectores que quieran implementar una estrategia similar deberían tener en cuenta:

1. Hacer un seguimiento de más eventos relacionados con la experiencia de carga

Realizamos un seguimiento de varios eventos que corresponden a una métrica técnica (p.ej., HTMLImportsLoaded, WebComponentsReady, etc.), pero como gran parte de la carga se realizó de forma asíncrona, el punto en el que se activaron estos eventos no necesariamente correspondía a un momento determinado de la experiencia de carga general.

El evento principal relacionado con la carga del que no realizamos un seguimiento (pero nos gustaría haberlo hecho) es el punto en el que desapareció la pantalla de presentación y el usuario pudo ver el contenido de la página.

2. Almacena el ID de cliente de Analytics en IndexedDB

De forma predeterminada, analytics.js almacena el campo de ID de cliente en las cookies del navegador. Lamentablemente, las secuencias de comandos de service worker no pueden acceder a las cookies.

Esto nos presentó un problema cuando intentamos implementar el seguimiento de notificaciones. Queríamos enviar un evento desde el trabajador de servicio (a través del Protocolo de medición) cada vez que se enviaba una notificación a un usuario y, luego, hacer un seguimiento del éxito de la reactivación de la participación de esa notificación si el usuario hacía clic en ella y regresaba a la app.

Si bien pudimos hacer un seguimiento del éxito de las notificaciones en general a través del parámetro de campaña utm_source, no pudimos vincular una sesión de reactivación de la participación a un usuario en particular.

Para evitar esta limitación, podríamos haber almacenado el ID de cliente a través de IndexedDB en nuestro código de seguimiento y, luego, la secuencia de comandos del trabajador del servicio podría haber accedido a ese valor.

3. Permite que el service worker informe el estado en línea o sin conexión

Si inspeccionas navigator.onLine, sabrás si tu navegador puede conectarse al router o a la red de área local, pero no te indicará si el usuario tiene conectividad real. Además, como nuestra secuencia de comandos del servicio de trabajo en segundo plano de Analytics sin conexión simplemente volvió a reproducir los hits que fallaron (sin modificarlos ni marcarlos como fallidos), es probable que no hayamos registrado correctamente nuestro uso sin conexión.

En el futuro, debemos hacer un seguimiento del estado de navigator.onLine y de si el service worker volvió a reproducir el hit debido a una falla inicial de red. Esto nos dará una imagen más precisa del uso real sin conexión.

Conclusión

Este caso de éxito demostró que el uso del trabajador de servicio mejoró el rendimiento de carga de la aplicación web de Google I/O en una amplia variedad de navegadores, redes y dispositivos. También se demostró que, cuando se observa una distribución de los datos de carga en una amplia variedad de navegadores, redes y dispositivos, se obtienen muchas más estadísticas sobre cómo esta tecnología maneja situaciones reales y se descubren características de rendimiento que quizás no se esperaban.

Estas son algunas de las conclusiones clave del estudio de IOWA:

  • En promedio, las páginas se cargaron bastante más rápido cuando un service worker controlaba la página que sin él, tanto para los visitantes nuevos como para los recurrentes.
  • Las visitas a las páginas controladas por un service worker se cargaron casi al instante para muchos usuarios.
  • Cuando estaban inactivos, los trabajadores de servicio tardaban un poco en iniciarse. Sin embargo, un service worker inactivo aún tenía un mejor rendimiento que no tener uno.
  • El tiempo de inicio de un trabajador de servicio inactivo era más largo en dispositivos móviles que en computadoras.

Si bien los aumentos de rendimiento observados en una aplicación en particular suelen ser útiles para informar a la comunidad más amplia de desarrolladores, es importante recordar que estos resultados son específicos del tipo de sitio que es IOWA (un sitio de eventos) y del tipo de público que tiene IOWA (en su mayoría, desarrolladores).

Si implementas un trabajador de servicio en tu aplicación, es importante que implementes tu propia estrategia de medición para que puedas evaluar tu propio rendimiento y evitar regresiones futuras. Si es así, comparte tus resultados para que todos puedan beneficiarse.

Pies de página

  1. No es del todo justo comparar el rendimiento de nuestra implementación de la caché del trabajador del servicio con el rendimiento de nuestro sitio solo con la caché HTTP. Como estábamos optimizando IOWA para el trabajador de servicio, no dedicamos mucho tiempo a optimizarlo para la caché HTTP. Si lo hubiéramos hecho, los resultados probablemente habrían sido diferentes. Para obtener más información sobre cómo optimizar tu sitio para la caché HTTP, lee Cómo optimizar el contenido de manera eficiente.
  2. Según la forma en que tu sitio cargue sus estilos y contenido, es posible que el navegador pueda pintar antes de que el contenido o los estilos estén disponibles. En esos casos, firstpaint puede corresponder a una pantalla en blanco. Si usas firstpaint, es importante que te asegures de que corresponda a un punto significativo en la carga de los recursos de tu sitio.
  3. Técnicamente, podríamos enviar un hit de tiempo (que no son de interacción de forma predeterminada) para capturar esta información en lugar de un evento. De hecho, los hits de tiempo se agregaron a Google Analytics específicamente para hacer un seguimiento de las métricas de carga como esta. Sin embargo, los hits de tiempo se muestrean mucho durante el tiempo de procesamiento y sus valores no se pueden usar en los segmentos. Dadas estas limitaciones actuales, los eventos sin interacción siguen siendo más adecuados.
  4. Para comprender mejor qué alcance debes asignar a una dimensión personalizada en Google Analytics, consulta la sección Dimensión personalizada del Centro de ayuda de Analytics. También es importante comprender el modelo de datos de Google Analytics, que consta de usuarios, sesiones e interacciones (hits). Para obtener más información, mira la lección de Analytics Academy sobre el modelo de datos de Google Analytics.
  5. Esto no tiene en cuenta los recursos cargados de forma diferida después del evento de carga.