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

Uno de los beneficios más importantes de los service workers (al menos desde la perspectiva del rendimiento) es su capacidad para controlar de forma proactiva el almacenamiento en caché de los recursos. Una aplicación web que pueda almacenar en caché todos sus recursos necesarios debería cargarse considerablemente más rápido para los visitantes recurrentes. Pero ¿cómo son realmente estas ganancias para los usuarios reales? ¿Y cómo se mide esto?

La app web de Google I/O (IOWA) es una app web progresiva que aprovechó la mayoría de las funciones nuevas que ofrecen los service workers para brindar una experiencia enriquecida, similar a la de una app, a sus usuarios. También utilizó Google Analytics para obtener datos clave de rendimiento y patrones de uso de un público amplio y diverso de usuarios.

Este caso de éxito explora cómo IOWA utilizó Google Analytics para responder preguntas clave sobre el rendimiento e informar sobre el impacto en el mundo real de los service workers.

Empezar con las preguntas

Cada vez que implementas Analytics en un sitio web o una aplicación, es importante empezar 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 este caso práctico, nos enfocaremos en dos de las más interesantes.

1. ¿El almacenamiento en caché del service worker tiene mejor rendimiento que los mecanismos existentes de almacenamiento en caché HTTP disponibles en todos los navegadores?

Esperamos que las páginas se carguen más rápido para los visitantes recurrentes que para los visitantes nuevos, ya que los navegadores pueden almacenar las solicitudes en caché y mostrarlas de forma instantánea en las visitas repetidas.

Los service workers ofrecen capacidades de almacenamiento en caché alternativas que brindan a los desarrolladores un control detallado sobre qué y cómo se realiza exactamente el almacenamiento en caché. En IOWA, optimizamos la implementación de service worker para que todos los recursos se almacenaran en caché, de manera que los visitantes que regresaban pudieran usar la app completamente sin conexión.

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

2. ¿Cómo afecta el service worker la experiencia de carga del sitio?

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

Obviamente, no es una tarea fácil responder preguntas sobre cómo se siente una experiencia, y ninguna métrica representará a la perfección una opinión tan subjetiva. Dicho esto, definitivamente 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 CPC Tiempo de carga de la página

Duración Tiempo de carga de la página es una métrica adecuada para responder nuestra primera pregunta, pero no es una métrica particularmente buena para responder la segunda. Por un lado, el evento load no necesariamente corresponde al momento en que el usuario puede interactuar con la app. Además, es posible que dos aplicaciones con el mismo tiempo de carga parezca que se cargan de una forma muy diferente. Por ejemplo, un sitio con una pantalla de presentación o un indicador de carga probablemente sienta que se carga mucho más rápido que un sitio que solo muestra una página en blanco por varios segundos.

En IOWA, mostramos una animación de cuenta regresiva de la pantalla de presentación que, en mi opinión, funcionó muy bien para entretener al usuario mientras el resto de la app se cargaba en segundo plano. Por este motivo, 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 percibida. Elegimos la métrica tiempo del primer procesamiento de imagen para obtener este valor.

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

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 la biblioteca analytics.js de forma asíncrona.

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 visitan tu sitio, pero no mucho más. Si deseas realizar un seguimiento de las interacciones adicionales de los usuarios, debes hacerlo tú mismo.

En cuanto a IOWA, queríamos hacer un seguimiento de dos aspectos adicionales:

  • El tiempo que transcurre desde que la página comienza a cargarse por primera vez hasta que aparecen píxeles en la pantalla.
  • Indica si un service worker controla la página o no. Con esa información, podríamos segmentar nuestros informes para comparar los resultados con y sin el service worker.

Captura el tiempo del primer procesamiento de imagen

Algunos navegadores registran el momento preciso en el 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 una contabilización muy precisa del tiempo que 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 para el primer procesamiento de imagen 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 obtienen los usuarios, y una buena primera impresión puede afectar positivamente 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, podemos escribir otra función que envíe un evento sin interacción con el tiempo de la primera pintura como su 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 estas dos funciones, nuestro código de seguimiento se verá 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. Para asegurarnos de ejecutar siempre este código después de 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 análisis hasta después de que se cargue la página para asegurarnos de 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 esa es solo la mitad del proceso. De todos modos, teníamos que hacer un seguimiento del estado del service worker; De lo contrario, no se podrían comparar los primeros tiempos de procesamiento de una página controlada por un service worker con una página no controlada.

Cómo determinar el estado de un service worker

Para determinar el estado actual del service worker, creamos una función de utilidad que muestra uno de tres valores:

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

Esta función obtuvo el estado del service worker por nosotros: el siguiente paso era asociar este estado con los datos que enviábamos a Google Analytics.

Cómo hacer 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. Las dimensiones comunes que les interesan a los desarrolladores web son, por ejemplo, el navegador, el sistema operativo o la categoría del dispositivo.

El estado del service worker no es una dimensión que Google Analytics proporciona de forma predeterminada. Sin embargo, Google Analytics te permite crear tus propias dimensiones personalizadas y definirlas como quieras.

Para IOWA, creamos una dimensión personalizada llamada Estado del service worker y establecimos su alcance en hit (es decir, por interacción).4 Cada dimensión personalizada que crea en Google Analytics recibe un índice único dentro de esa propiedad, y en su código de seguimiento puede 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 de modo que incluya el estado del service worker:

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 service worker con este evento en particular. Dado que el estado del service worker es algo que podría ser útil saber en cualquier interacción, te recomendamos que lo incluyas junto con todos los datos que se envíen a Google Analytics.

Para incluir esta información en todos los hits (p.ej., todas las vistas de página, eventos, etc.), configuramos 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 tarde, es probable que se muestre un valor nuevo desde la función getServiceWorkerStatus() y que ese valor se establezca en el objeto de seguimiento.

Una aclaración breve sobre la claridad y legibilidad del código: como es posible que otras personas que miren 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 Service Worker Status con cada hit nos permite usarla cuando generamos informes sobre cualquier métrica.

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

Los resultados: respondiendo nuestras preguntas

Una vez que empezamos 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 entre el 16 y el 22 de mayo de 2016).

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

Para responder a esta pregunta, creamos un informe personalizado que analizaba la métrica Posición Tiempos de carga de la página en varias dimensiones. Esta métrica es adecuada para responder a esta pregunta, ya que 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 esenciales del sitio.5

Las dimensiones que elegimos fueron las siguientes:

  • Nuestra dimensión personalizada Estado de service worker.
  • Tipo de usuario, que indica si esta es la primera visita del usuario al sitio o si está volviendo (Nota: Un visitante nuevo no tendrá ningún recurso en caché; un visitante recurrente podría hacerlo).
  • Device Category, que nos permite comparar los resultados en dispositivos móviles y computadoras de escritorio.

Para controlar la posibilidad de que los factores que no están relacionados con service worker sesgaran los resultados de nuestro tiempo de carga, limitamos nuestra consulta para que incluya solo los navegadores que admitan service worker.

Como puedes ver, las visitas a nuestra aplicación controladas por un service worker se cargan un poco 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 que utilizan dispositivos móviles con un service worker registran cargas más rápidas que los nuevos visitantes de computadoras de escritorio.

"... las visitas a nuestra app cuando son controladas por un service worker se cargan un poco más rápido que las visitas no controladas..."

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

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

Quizás te estés preguntando cómo es posible que un visitante recurrente cuyo navegador admite un service worker esté en estado no controlado. Hay algunas posibles explicaciones para esto:

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

Ambas situaciones son relativamente raras. Podemos verlo en los datos si observamos los valores de Ejemplo de carga de página 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 service worker la experiencia de carga del sitio?

Para responder a esta pregunta, creamos otro informe personalizado para la métrica Posición Event Value y filtró los resultados para incluir solo nuestros eventos firstpaint. Usamos las dimensiones Categoría de dispositivo y nuestra dimensión personalizada Estado del service worker.

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

"...el service worker en dispositivos móviles tuvo mucho menos impacto en el tiempo de procesamiento del primer procesamiento de imagen que en la carga general de la página".

Para explorar a qué se debe este problema, debemos profundizar en los datos. Los promedios pueden ser útiles en el caso de descripciones generales y de trazos generales, pero para tener una idea de cómo se desglosan estos números en un rango de usuarios, debemos observar una distribución de firstpaint veces.

Obtén la distribución de una métrica en Google Analytics

Para obtener la distribución de 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 según la dimensión que queramos, pero no usa el desglose por métricas. Eso no quiere decir que sea imposible, simplemente significa que tuvimos que personalizar un poco más nuestra implementación para obtener el resultado deseado.

Dado que los resultados del informe solo se pueden desglosar por dimensiones, tuvimos que establecer el valor de la métrica (en este caso, firstpaint vez) como una dimensión personalizada en el evento. Para ello, creamos otra dimensión personalizada llamada Valor de la 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 ofrece una forma de visualizar la distribución de valores arbitrarios de métricas, pero con la ayuda de la API de Google Analytics Core Reporting y la biblioteca de Google Charts, podríamos consultar los resultados sin procesar y, luego, crear un histograma.

Por ejemplo, se usó la siguiente configuración de solicitud a la API para obtener una distribución de valores firstpaint en computadoras de escritorio con un service worker 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 con el siguiente aspecto (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:dimensión2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

Esto es lo que significan estos resultados directamente en inglés:

  • 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 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 value de firstpaint fue de 8 ms
  • etc.

A partir de estos resultados, podemos extrapolar el valor 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 una computadora de escritorio con un service worker no controlado (pero compatible):

Distribución del tiempo hasta el primer procesamiento de imagen en computadoras (compatible)

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

La forma de esta curva es típica de las distribuciones de tiempo de carga. Compara eso con el histograma a continuación, que muestra la distribución de los eventos de primer procesamiento de imagen para las visitas en las que un service worker controla la página.

Distribución del tiempo transcurrido hasta el primer procesamiento de imagen en computadoras de escritorio (controlado)

Ten en cuenta que, cuando un service worker controla la página, muchos visitantes experimentaron un primer procesamiento de imagen casi inmediato, 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 las dos. El histograma que muestra las visitas de service worker no controladas se superpone al histograma que muestra las visitas controladas, y ambos se superponen a un histograma que muestra ambos combinados.

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

Un aspecto interesante de estos resultados es que la distribución con un service worker controlado aún tenía una curva en forma de campana después del aumento repentino inicial. Esperaba un aumento repentino alto y, luego, un descenso gradual. No esperaba un segundo pico en la curva.

Cuando investigamos cuál podría ser la causa de esto, me di cuenta de que, aunque un service worker puede estar controlando una página, su subproceso puede estar inactivo. El navegador hace esto para ahorrar recursos. Obviamente, no es necesario que todos los service workers de todos los sitios que visitaste estén activos y listos en todo momento. Esto explica la cola de la distribución. Para algunos usuarios, hubo un retraso mientras se iniciaba el subproceso del service worker.

Sin embargo, como puedes ver en la distribución, incluso con este retraso inicial, los navegadores con service worker entregaban contenido más rápido que los que pasaban por la red.

A continuación, te mostramos cómo se veían en los dispositivos móviles:

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

Si bien obtuvimos un aumento considerable en los tiempos de primera pintura casi inmediatos, la cola era un poco más grande y más larga. Es probable que esto se deba a que, en un dispositivo móvil, iniciar un subproceso de service worker inactivo tarda más que en una computadora de escritorio. También explica por qué la diferencia entre el tiempo promedio de firstpaint no era tan grande como esperaba (se explica más arriba).

"... en dispositivos móviles, iniciar un subproceso de service worker inactivo tarda más que en una computadora de escritorio."

A continuación, se muestra el desglose de estas variaciones de la mediana del tiempo de primer procesamiento de imagen en dispositivos móviles y computadoras, agrupadas por estado de service worker:

Tiempo medio hasta la primera pintura (ms)
Estado del service worker Computadora de escritorio Dispositivos móviles
Controlaste 583 1634
Compatible (no controlado) 912 1933

Si bien crear estas visualizaciones de distribución requirió un poco más de tiempo y esfuerzo que crear un informe personalizado en Google Analytics, nos dan una idea mucho mejor de cómo los service workers afectan el rendimiento de nuestro sitio en comparación con los promedios por sí solos.

Otros impactos de los service workers

Además del impacto en el rendimiento, los service workers también influyen en la experiencia del usuario de muchas otras formas que se pueden medir con Google Analytics.

Acceso sin conexión

Los service workers permiten a los usuarios interactuar con tu sitio mientras están sin conexión y, si bien es probable que algún tipo de soporte sin conexión sea fundamental para cualquier app web progresiva, determinar su importancia depende en gran medida de cuánto uso ocurre sin conexión. Pero ¿cómo medimos eso?

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 tuvo lugar la interacción. Google Analytics admite el envío de datos de interacción después del hecho mediante la especificación de una compensación horaria (a través del parámetro qt).

Durante los últimos dos años, IOWA ha utilizado una secuencia de comandos de service worker que detecta hits con errores de Google Analytics cuando el usuario no está conectado y los reproduce más tarde con el parámetro qt.

Para realizar 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 establecimos con el valor de navigator.onLine. Luego, escuchamos los eventos online y offline, y actualizamos la dimensión según corresponda.

Además, para tener una idea de lo habitual que era que un usuario se encontrara sin conexión mientras utilizaba IOWA, creamos un segmento orientado a los usuarios con, al menos, una interacción sin conexión. Resulta que representaba casi el 5% de los usuarios.

Notificaciones push

Los service workers permiten a los usuarios habilitar la recepción de notificaciones push. En IOWA, se notificó a los usuarios cuando una sesión en su programa estaba a punto de comenzar.

Al igual que con cualquier forma de notificación, es importante encontrar el equilibrio entre brindar valor al usuario y molestarlo. Para comprender mejor lo que sucede, es importante realizar un seguimiento para saber si los usuarios aceptaron recibir estas notificaciones, si interactúan con ellos cuando llegan y si algún usuario que anteriormente las habilitó cambia sus preferencias y las rechazan.

En IOWA, solo enviamos notificaciones relacionadas con la agenda personalizada de los usuarios, algo que solo podían crear los usuarios que accedieran a sus cuentas. Esto limitaba el conjunto de usuarios que podían recibir notificaciones para los usuarios que accedieron (se realiza un seguimiento mediante una dimensión personalizada denominada Acceso) cuyos navegadores admiten notificaciones push (se realiza un seguimiento mediante otra dimensión personalizada llamada Permiso de notificación).

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 fantástico ver que más de la mitad de los usuarios que accedieron a sus cuentas decidieron recibir notificaciones push.

Banners de instalación de apps

Si una app web de progreso 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 en el que se le solicite que agregue la app a su pantalla principal.

En IOWA, realizamos un seguimiento de la frecuencia con la que se mostraron estos mensajes al usuario (y si se aceptaron) 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 estadísticos que recopilamos de IOWA este año fueron invaluables. Sin embargo, la retrospectiva siempre arroja luz y oportunidades para mejorar la próxima vez. Después de finalizar el análisis de este año, te contamos dos cosas que me gustaría haber hecho de manera diferente y que los lectores que quieran implementar una estrategia similar podrían considerar:

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

Hicimos 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 se correspondería con un momento particular en la experiencia de carga general.

El evento principal relacionado con la carga del que no realizamos un seguimiento (pero que queríamos hacerlo) 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 estadísticas en IndexedDB

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

Esto presentó un problema cuando intentamos implementar el seguimiento de notificaciones. Queríamos enviar un evento del service worker (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 volvía a la app.

Si bien pudimos realizar un seguimiento del éxito de las notificaciones en general mediante el parámetro de campaña utm_source, no pudimos vincular una sesión específica para volver a generar participación con un usuario en particular.

Lo que podríamos haber hecho para solucionar esta limitación fue almacenar el ID de cliente a través de IndexedDB en nuestro código de seguimiento y, entonces, ese valor habría sido accesible para la secuencia de comandos del service worker.

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

Si inspeccionas navigator.onLine, podrás saber si el navegador puede conectarse al router o a la red de área local, pero no necesariamente sabrá si el usuario tiene conectividad real. Y debido a que nuestra secuencia de comandos del service worker de análisis sin conexión simplemente volvió a reproducir hits fallidos (sin modificarlos ni marcarlos como fallidos), es probable que no tengamos informes de nuestro uso sin conexión.

En el futuro, deberíamos 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 la red. Esto nos dará una idea más precisa del verdadero uso sin conexión.

Conclusión

Este caso práctico demostró que el uso de service worker realmente mejoró el rendimiento de carga de la aplicación web de Google I/O en una amplia gama de navegadores, redes y dispositivos. También demostró que cuando se observa una distribución de los datos de carga en una amplia variedad de navegadores, redes y dispositivos, se obtiene mucha más información sobre cómo esta tecnología maneja situaciones del mundo real y se descubren características de rendimiento que quizás no esperabas.

Estas fueron algunas de las conclusiones clave del estudio IOWA:

  • En promedio, las páginas se cargaban un poco más rápido cuando un service worker controlaba la página que sin un service worker, tanto para los visitantes nuevos como para los recurrentes.
  • Las visitas a páginas controladas por un service worker se cargan casi al instante para muchos usuarios.
  • Cuando los service workers están inactivos, tardan un poco en iniciarse. Sin embargo, un service worker inactivo aún tenía un mejor rendimiento que ningún service worker.
  • El tiempo de inicio de un service worker inactivo fue mayor en los dispositivos móviles que en las computadoras de escritorio.

Si bien las mejoras en el rendimiento observadas 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 para el tipo de sitio de IOWA (un sitio de eventos) y el tipo de público que tiene (en su mayoría, los desarrolladores).

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

Pies de página

  1. No es del todo justo comparar el rendimiento de la implementación de la caché de nuestro service worker con el rendimiento de nuestro sitio solo con la caché HTTP. Debido a que optimizábamos IOWA para el service worker, no perdimos mucho tiempo optimizando para la caché HTTP. Si hubiéramos obtenido, los resultados probablemente habrían sido diferentes. Si quieres obtener más información sobre cómo optimizar tu sitio para el almacenamiento en caché HTTP, consulta Cómo optimizar el contenido de manera eficiente.
  2. Según cómo cargue tu sitio su estilo 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 blanca en blanco. Si usas firstpaint, es importante que te asegures de que corresponda a un punto significativo en la carga de recursos de tu sitio.
  3. Técnicamente, podríamos enviar un hit de timing (que no son 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 en gran medida en el momento 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 darle 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, interacciones (hits) y sesiones. Para obtener más información, mira la lección de Analytics Academy sobre el modelo de datos de Google Analytics.
  5. Esto no considera los recursos que se cargan de forma diferida después del evento de carga.