Como medir o impacto do desempenho no mundo real dos service workers

Um dos benefícios mais significativos dos service workers (pelo menos do ponto de vista do desempenho) é a capacidade de controlar proativamente o armazenamento em cache dos recursos. Um aplicativo da web que pode armazenar em cache todos os seus recursos necessários deve carregar de forma significativamente mais rápida para os visitantes que retornam. Mas o que esses ganhos realmente parecem para os usuários reais? E como você mede isso?

O web app Google I/O (IOWA, na sigla em inglês) é um Progressive Web App que aproveita a maioria dos novos recursos oferecidos pelos service workers para oferecer aos usuários uma experiência rica, semelhante a um app. Ela também usou o Google Analytics para capturar os principais dados de desempenho e padrões de uso do grande e diversificado público de usuários.

Este estudo de caso explora como a IOWA usou o Google Analytics para responder às principais perguntas sobre desempenho e gerar relatórios sobre o impacto real dos service workers.

Começar com as perguntas

Sempre que você implementar a análise em um site ou aplicativo, é importante começar identificando as perguntas a serem respondidas a partir dos dados que serão coletados.

Embora tivéssemos várias perguntas que queríamos responder, para os propósitos deste estudo de caso, vamos nos concentrar em duas das mais interessantes.

1. O armazenamento em cache do service worker tem melhor desempenho do que os mecanismos de cache HTTP atuais disponíveis em todos os navegadores?

Já esperamos que as páginas sejam carregadas mais rapidamente para os visitantes recorrentes do que para os novos, pois os navegadores podem armazenar as solicitações em cache e exibi-las instantaneamente em visitas repetidas.

Os service workers oferecem recursos alternativos de armazenamento em cache que dão aos desenvolvedores controle detalhado sobre exatamente o que e como o armazenamento em cache é feito. No IOWA, otimizamos a implementação do nosso service worker para que todos os recursos fossem armazenados em cache, assim os visitantes recorrentes pudessem usar o app completamente off-line.

Mas esse esforço seria melhor do que o que o navegador já faz por padrão? Em caso afirmativo, como seria melhor? 1

2. Como o service worker afeta a experiência de carregamento do site?

Em outras palavras, qual a velocidade de carregamento do site, independentemente dos tempos de carregamento reais medidos pelas métricas tradicionais de carregamento de página?

Responder perguntas sobre como é uma experiência obviamente não é uma tarefa fácil, e nenhuma métrica vai representar perfeitamente um sentimento tão subjetivo. Dito isso, definitivamente há algumas métricas que são melhores do que outras, por isso escolher as certas é importante.

Como escolher a métrica certa

Por padrão, o Google Analytics acompanha os tempos de carregamento da página (por meio da API Navigation Timing) para 1% dos visitantes de um site e disponibiliza esses dados por meio de métricas como "Tempo tempo de carregamento da página.

Média de O tempo de carregamento da página é uma boa métrica para responder à primeira pergunta, mas não é uma boa métrica para responder à segunda. O evento load não corresponde necessariamente ao momento em que o usuário pode interagir com o app. Além disso, dois apps com exatamente o mesmo tempo de carregamento podem parecer carregar de maneira muito diferente. Por exemplo, um site com uma tela de apresentação ou um indicador de carregamento provavelmente parece carregar muito mais rápido do que um site que mostra apenas uma página em branco por vários segundos.

No IOWA, mostramos uma animação de contagem regressiva na tela de apresentação que, na minha opinião, serviu para entreter o usuário enquanto o restante do app era carregado em segundo plano. Por isso, rastrear quanto tempo a tela de apresentação leva para aparecer faz muito mais sentido como uma maneira de medir o desempenho de carregamento percebido. Escolhemos a métrica tempo da primeira pintura para chegar a esse valor.

Depois de decidir as perguntas que queríamos responder e identificar as métricas que seriam úteis para respondê-las, era hora de implementar o Google Analytics e começar a avaliar.

Implementação da análise de dados

Se você já usou o Google Analytics antes, provavelmente já conhece o snippet de acompanhamento JavaScript recomendado. Esta é a aparência dela:

<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>

A primeira linha no código acima inicializa uma função ga() global (se ela ainda não existir) e a última linha faz o download da biblioteca analytics.js de forma assíncrona.

A parte do meio contém estas duas linhas:

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

Esses dois comandos acompanham quais páginas são acessadas pelas pessoas que acessam seu site, mas não muito mais. Se quiser acompanhar outras interações do usuário, você terá que fazê-lo por conta própria.

Para a IOWA, queríamos acompanhar duas coisas adicionais:

  • É o tempo decorrido entre o início do carregamento da página e a exibição dos pixels na tela.
  • Se um service worker está controlando a página ou não. Com essas informações, podemos segmentar nossos relatórios para comparar os resultados com e sem service worker.

Como registrar o tempo da first paint

Alguns navegadores registram o horário exato em que o primeiro pixel é exibido na tela e o disponibilizam aos desenvolvedores. Em comparação com o valor de navigationStart exposto pela API Navigation Timing, esse valor nos oferece uma contabilização muito precisa de quanto tempo se passou entre o momento em que o usuário solicitou a página pela primeira vez e quando ele viu algo pela primeira vez.

Como já mencionei, o tempo da first paint é uma métrica importante a ser medida, pois é o primeiro ponto em que um usuário experimenta a velocidade de carregamento do seu site. É a primeira impressão que os usuários têm, e uma boa primeira impressão pode afetar positivamente o restante da experiência do usuário.2

Para gerar o valor da first paint nos navegadores que o expõem, criamos a função utilitária 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;
    }
  }
}

Com isso, podemos escrever outra função que envia um evento sem interação com o tempo da first paint como seu 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
    });
  }
}

Depois de escrever essas duas funções, nosso código de acompanhamento ficará assim:

// 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();

Observe que, dependendo de quando o código acima é executado, os pixels podem ou não ter sido pintados na tela. Para garantir que esse código seja sempre executado após a first paint, adiamos a chamada para sendTimeToFirstPaint() para depois do evento load. Na verdade, decidimos adiar o envio de todos os dados de análise para depois que a página foi carregada para garantir que essas solicitações não concorram com o carregamento de outros 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();
});

O código acima informa firstpaint vezes ao Google Analytics, mas isso é apenas metade do processo. Ainda precisávamos rastrear o status do service worker, caso contrário, não poderíamos comparar os tempos da first paint de uma página controlada pelo service worker e de uma página não controlada.

Como determinar o status do service worker

Para determinar o status atual do service worker, criamos uma função utilitária que retorna um destes três valores:

  • controlada: um service worker está controlando a página. No caso do IOWA, isso também significa que todos os recursos foram armazenados em cache e que a página funciona off-line.
  • supported: o navegador oferece suporte ao service worker, mas ele ainda não controla a página. Esse é o status esperado para visitantes novos.
  • unsupported: o navegador do usuário não oferece suporte ao service worker.
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

Essa função gerou o status de service worker para nós. a próxima etapa foi associar esse status aos dados que estávamos enviando ao Google Analytics.

Como rastrear dados personalizados com dimensões personalizadas

Por padrão, o Google Analytics oferece muitas maneiras de subdividir seu tráfego total em grupos com base nos atributos do usuário, da sessão ou da interação. Esses atributos são conhecidos como dimensões. Dimensões comuns que são importantes para os desenvolvedores da Web são Navegador, Sistema operacional ou Categoria do dispositivo.

O status do service worker não é uma dimensão que o Google Analytics fornece por padrão. No entanto, o Google Analytics permite criar suas próprias dimensões personalizadas e defini-las como você quiser.

Para a IOWA, criamos uma dimensão personalizada chamada Status do service worker e definimos o escopo dela como hit (por interação).4 Cada dimensão personalizada que você cria no Google Analytics recebe um índice exclusivo nessa propriedade, e no seu código de acompanhamento você pode fazer referência a essa dimensão pelo índice dela. Por exemplo, se o índice da dimensão que acabamos de criar fosse 1, poderíamos atualizar nossa lógica da seguinte maneira para enviar o evento firstpaint e incluir o status do 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()
});

Isso funciona, mas só associa o status do service worker a esse evento específico. Como o Status do service worker é algo potencialmente útil para qualquer interação, é melhor incluí-lo em todos os dados enviados ao Google Analytics.

Para incluir essas informações em todos os hits (por exemplo, todas as visualizações de página, eventos etc.), definimos o valor da dimensão personalizada no próprio objeto tracker antes de enviar os dados ao Google Analytics.

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

Depois de definido, esse valor é enviado com todos os hits subsequentes para o carregamento de página atual. Se o usuário carregar a página novamente mais tarde, um novo valor provavelmente será retornado da função getServiceWorkerStatus() e definido no objeto do rastreador.

Uma observação rápida sobre clareza e legibilidade do código: já que outras pessoas que visualizam esse código podem não saber a que dimension1 se refere, é sempre melhor criar uma variável que associe nomes de dimensões significativos aos valores que a analytics.js usará.

// 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 mencionei, enviar a dimensão Status do worker do serviço com cada hit permite usá-la ao gerar relatórios sobre qualquer métrica.

Como você pode ver, quase 85% de todas as visualizações de página de IOWA são de navegadores que oferecem suporte ao service worker.

Os resultados: respostas às nossas perguntas

Depois que começamos a coletar dados para responder às nossas perguntas, poderíamos fazer um relatório sobre esses dados para ver os resultados. Observação: todos os dados do Google Analytics exibidos aqui representam o tráfego real da Web para o site da IOWA de 16 a 22 de maio de 2016.

A primeira pergunta que tivemos foi: o armazenamento em cache do service worker tem um desempenho melhor do que os mecanismos de cache HTTP disponíveis em todos os navegadores?

Para responder a essa pergunta, criamos um relatório personalizado que analisava a métrica Média de tempos de carregamento de página em várias dimensões. Essa métrica é adequada para responder a essa pergunta porque o evento load é disparado somente depois que todos os recursos iniciais são baixados. Assim, ele reflete diretamente o tempo total de carregamento de todos os recursos essenciais do site.5

As dimensões que escolhemos foram:

  • Nossa dimensão personalizada Status do service worker
  • Tipo de usuário, que indica se essa é a primeira visita do usuário ao site ou se ele está retornando. Observação: um novo visitante não terá recursos em cache, ao contrário de um visitante recorrente.
  • Categoria do dispositivo: permite comparar os resultados em dispositivos móveis e computadores.

Para controlar a possibilidade de fatores não relacionados ao service worker distorcerem nossos resultados de tempo de carregamento, limitamos a consulta para incluir apenas navegadores que oferecem suporte ao service worker.

Como você pode ver, as visitas ao nosso aplicativo quando controladas por um service worker são carregadas muito mais rapidamente do que as visitas não controladas, mesmo aquelas de usuários recorrentes que provavelmente tinham a maioria dos recursos da página armazenados em cache. Também é interessante notar que, em média, os visitantes em dispositivos móveis com um service worker notaram carregamentos mais rápidos do que os novos visitantes de computadores.

"... as visitas ao nosso app quando controladas por um service worker são carregadas muito mais rapidamente do que as visitas não controladas..."

Confira mais detalhes nas duas tabelas a seguir:

Duração média Tempo de carregamento da página (computadores)
Status do service worker Tipo de usuário Tempo médio de carregamento da página (ms) Tamanho da amostra
Controlou Visitante recorrente 2568 30860
Compatível Visitante recorrente 3612 1289
Compatível Novo visitante 4664 21991
Duração média Tempo de carregamento da página (dispositivos móveis)
Status do service worker Tipo de usuário Tempo médio de carregamento da página (ms) Tamanho da amostra
Controlou Visitante recorrente 3760 8162
Compatível Visitante recorrente 4843 676
Compatível Novo visitante 6158 5779

Você pode estar se perguntando como é possível que um visitante recorrente cujo navegador ofereça suporte ao service worker fique em um estado não controlado. Existem algumas explicações possíveis para isso:

  • O usuário saiu da página na visita inicial antes que o service worker tivesse a chance de concluir a inicialização.
  • O usuário desinstalou o service worker usando as ferramentas para desenvolvedores.

Ambas as situações são relativamente raras. Nos dados, podemos ver isso ao observar os valores de Amostra de carregamento de página na quarta coluna. Observe que as linhas do meio têm uma amostra muito menor que as outras duas.

Nossa segunda pergunta foi: Como o service worker afeta a experiência de carregamento do site?

Para responder a essa pergunta, criamos outro relatório personalizado para a métrica Média de "Valor do evento" e filtrou os resultados para incluir apenas nossos eventos firstpaint. Usamos as dimensões Categoria do dispositivo e nossa dimensão personalizada Status do worker do serviço.

Ao contrário do que eu esperava, o service worker em dispositivos móveis teve muito menos impacto no tempo da first paint do que no carregamento geral da página.

"...o service worker em dispositivos móveis teve muito menos impacto no tempo da first paint do que no carregamento geral da página."

Para entender por que isso acontece, precisamos analisar melhor os dados. As médias podem ser boas para informações gerais e gerais, mas para realmente ter uma noção de como esses números se dividem em um intervalo de usuários, precisamos analisar uma distribuição de firstpaint vezes.

Receber a distribuição de uma métrica no Google Analytics

Para conseguir a distribuição de firstpaint vezes, precisamos de acesso aos resultados individuais de cada evento. Infelizmente, o Google Analytics não facilita isso.

O Google Analytics permite dividir um relatório por qualquer dimensão desejada, mas não permite dividir um relatório por métricas. Isso não quer dizer que seja impossível, mas significa que tivemos que personalizar um pouco mais a implementação para chegar ao resultado desejado.

Como os resultados do relatório só podem ser detalhados por dimensões, tivemos que definir o valor da métrica (nesse caso, firstpaint vez) como uma dimensão personalizada no evento. Para isso, criamos outra dimensão personalizada chamada Valor da métrica e atualizamos nossa lógica de acompanhamento firstpaint da seguinte maneira:

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);
  }
}

Atualmente, a interface da Web do Google Analytics não oferece uma maneira de visualizar a distribuição de valores de métricas arbitrárias, mas, com a ajuda da API de relatórios principais do Google Analytics e da biblioteca de gráficos do Google, podemos consultar os resultados brutos e construir um histograma por conta própria.

Por exemplo, a configuração de solicitação de API a seguir foi usada para receber uma distribuição de valores firstpaint no computador com um service worker não 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'
    }
  ]
}

Essa solicitação de API retorna uma matriz de valores que se parecem com o exemplo a seguir. Observação: esses são apenas os cinco primeiros resultados. Os resultados são classificados do menor para o maior, de modo que essas linhas representam os tempos mais rápidos.

Resultados da resposta da API (primeiras linhas)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

Veja o que esses resultados significam em português:

  • Houve 3 eventos em que o valor de firstpaint foi de 4 ms
  • Houve dois eventos em que o valor de firstpaint foi de 5 ms
  • Houve 10 eventos em que o valor de firstpaint foi de 6 ms
  • Ocorreram 8 eventos em que o valor de firstpaint foi de 7 ms
  • Houve 10 eventos em que a value de firstpaint foi de 8 ms
  • etc.

A partir desses resultados, podemos extrapolar o valor de firstpaint para cada evento e criar um histograma da distribuição. Fizemos isso para cada uma das consultas que executamos.

Veja como é a distribuição no computador com um service worker não controlado (mas compatível):

Distribuição de tempo para a first paint no computador (compatível)

O tempo firstpaint médio para a distribuição acima é de 912 ms.

O formato dessa curva é bem parecido com as distribuições de tempo de carregamento. Compare isso com o histograma abaixo, que mostra a distribuição de eventos de first paint para as visitas em que um service worker estava controlando a página.

Distribuição do tempo até a first paint no computador (controlado)

Quando um service worker estava controlando a página, muitos visitantes tiveram uma first paint quase imediata, com uma mediana de 583 ms.

"...quando um service worker estava controlando a página, muitos visitantes experimentaram uma first paint quase imediata..."

Para ter uma noção melhor de como essas duas distribuições se comparam, o próximo gráfico mostra uma visão mesclada das duas. O histograma que mostra visitas não controladas ao service worker é sobreposto ao histograma que mostra as visitas controladas, e ambos estão sobrepostos em um histograma que mostra ambas combinadas.

Distribuição do tempo para a first paint no computador

Uma coisa que achei interessante sobre esses resultados foi que a distribuição com um service worker controlado ainda tinha uma curva em forma de sino após o pico inicial. Eu esperava um grande pico inicial e depois uma trilha gradual, não esperava um segundo pico na curva.

Quando analisei o que poderia estar causando isso, aprendi que mesmo que um service worker possa estar controlando uma página, sua sequência pode estar inativa. O navegador faz isso para economizar recursos. Obviamente, você não precisa que todos os service workers de todos os sites que você já visitou estejam ativos e prontos a qualquer momento. Isso explica a cauda da distribuição. Para alguns usuários, houve um atraso enquanto a linha de execução do service worker era iniciada.

No entanto, como é possível ver na distribuição, mesmo com esse atraso inicial, os navegadores com o service worker entregaram conteúdo mais rapidamente do que os navegadores que passavam pela rede.

Veja como aparecia no dispositivo móvel:

Distribuição do tempo para a first paint em dispositivos móveis

Embora ainda tivéssemos um aumento considerável nos tempos quase imediatos da first paint, a cauda era bem maior e mais longa. Isso provavelmente ocorre porque, em dispositivos móveis, iniciar uma linha de execução de service worker inativa demora mais do que em um computador. Ele também explica por que a diferença entre o tempo médio de firstpaint não é tão grande quanto eu esperava (discutido acima).

"... em dispositivos móveis, iniciar uma linha de execução de service worker inativa leva mais tempo do que em um computador."

Veja o detalhamento dessas variações de tempos médios de first paint em dispositivos móveis e computadores agrupados por status de service worker:

Tempo médio para a first paint (ms)
Status do service worker Computador Dispositivo móvel
Controlou 583 1634
Compatível (não controlado) 912 1933

Embora criar essas visualizações de distribuição exigisse um pouco mais de tempo e esforço do que criar um relatório personalizado no Google Analytics, elas nos dão uma noção muito melhor de como os service workers afetam o desempenho do nosso site do que as médias sozinhos.

Outro impacto dos service workers

Além do impacto no desempenho, os service workers também afetam a experiência do usuário de várias outras maneiras mensuráveis com o Google Analytics.

Acesso off-line

Os service workers permitem que os usuários interajam com seu site enquanto estão off-line e, embora algum tipo de suporte off-line provavelmente seja essencial para qualquer Progressive Web App, determinar a importância dele nesse caso depende muito da quantidade de uso off-line. Mas como medimos isso?

Para enviar dados ao Google Analytics, é preciso ter uma conexão de Internet, mas não no momento exato da interação. O Google Analytics oferece suporte ao envio de dados de interação após a realização, especificando um ajuste de horário (com o parâmetro qt).

Nos últimos dois anos, o IOWA tem usado um script de service worker que detecta hits com falha no Google Analytics quando o usuário está off-line e os reproduz depois com o parâmetro qt.

Para acompanhar se o usuário estava on-line ou off-line, criamos uma dimensão personalizada chamada On-line e a definimos como o valor navigator.onLine. Em seguida, ouvimos os eventos online e offline e atualizamos a dimensão de acordo.

Para ter uma noção de como era comum um usuário ficar off-line ao usar o IOWA, criamos um segmento que segmenta usuários com pelo menos uma interação off-line. Acontece que quase 5% dos usuários.

Notificações push

Os service workers permitem que os usuários ativem o recebimento de notificações push. No IOWA, os usuários foram notificados quando uma sessão da programação deles estava prestes a começar.

Como em qualquer forma de notificação, é importante encontrar o equilíbrio entre oferecer valor ao usuário e incomodá-lo. Para entender melhor o que está acontecendo, é importante acompanhar se os usuários ativam o recebimento dessas notificações, se estão interagindo com elas quando chegam e se algum usuário que já ativou mudou as preferências ou desistiu.

No IOWA, enviamos apenas notificações relacionadas à programação personalizada do usuário, algo que apenas usuários conectados poderiam criar. Isso limitava o conjunto de usuários que podiam receber notificações (rastreadas por uma dimensão personalizada chamada Conectado) com navegadores compatíveis com notificações push (rastreadas por outra dimensão personalizada chamada Permissão de notificação).

O relatório a seguir se baseia na métrica Usuários e na nossa dimensão personalizada "Permissão de notificação", segmentada por usuários que fizeram login em algum momento e com navegadores compatíveis com notificações push.

É ótimo saber que mais da metade dos usuários conectados optou por receber notificações push.

Banners de instalação de apps

Se um app da Web que estiver em exibição atender aos critérios e for usado com frequência por um usuário, talvez ele veja um banner de instalação do app solicitando que adicione o app à tela inicial.

No IOWA, rastreamos com que frequência esses comandos eram mostrados ao usuário (e se foram aceitos) com o seguinte 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
    });
  });
});

Dos usuários que viram um banner de instalação de aplicativo, cerca de 10% optaram por adicioná-lo à tela inicial.

Possíveis melhorias de acompanhamento (para a próxima vez)

Os dados de análise que coletamos do IOWA neste ano foram inestimáveis. Mas a retrospectiva sempre traz buracos e oportunidades para melhorar as coisas da próxima vez. Depois de terminar a análise deste ano, aqui estão duas coisas que eu gostaria que tivéssemos feito diferente e que os leitores que desejam implementar uma estratégia semelhante poderiam considerar:

1. Rastrear mais eventos relacionados à experiência de carregamento

Acompanhamos vários eventos que correspondem a uma métrica técnica (por exemplo, HTMLImportsLoaded, WebComponentsReady etc.), mas como grande parte do carregamento foi feito de forma assíncrona, o ponto no qual esses eventos disparados não corresponde necessariamente a um momento específico na experiência geral de carregamento.

O evento principal relacionado ao carregamento que não acompanhamos (mas gostaria de ter tido) é o ponto em que a tela de apresentação desapareceu e o usuário podia ver o conteúdo da página.

2. Armazenar o ID do cliente de análise no IndexedDB

Por padrão, a analytics.js armazena o campo do ID do cliente nos cookies do navegador. Infelizmente, os scripts do service worker não podem acessar cookies.

Isso apresentou um problema quando tentamos implementar o rastreamento de notificações. Queríamos enviar um evento do service worker (pelo Measurement Protocol) sempre que uma notificação fosse enviada a um usuário e rastrear o sucesso do reengajamento dessa notificação se o usuário clicasse nela e voltasse ao app.

Embora pudéssemos acompanhar o sucesso das notificações em geral por meio do parâmetro de campanha utm_source, não foi possível vincular uma sessão específica de reengajamento a um usuário específico.

O que poderíamos ter feito para contornar essa limitação seria armazenar o ID do cliente por meio do IndexedDB em nosso código de acompanhamento para que esse valor ficasse acessível para o script do service worker.

3. Permitir que o service worker informe o status on-line/off-line

Ao inspecionar o navigator.onLine, você saberá se o navegador pode se conectar ao roteador ou à rede local, mas não necessariamente saberá se o usuário tem conectividade real. E como nosso script do service worker de análises off-line simplesmente repetiu hits com falha (sem modificá-los ou marcá-los como com falha), provavelmente não tínhamos relatórios do uso off-line.

No futuro, devemos rastrear o status de navigator.onLine e também se o hit foi repetido pelo service worker devido a uma falha inicial de rede. Isso nos proporciona uma imagem mais precisa do verdadeiro uso off-line.

Conclusão

Este estudo de caso mostrou que usar o service worker realmente melhorou o desempenho de carregamento do webapp do Google I/O em uma ampla variedade de navegadores, redes e dispositivos. Ele também mostrou que, quando você analisa uma distribuição dos dados de carregamento em uma ampla variedade de navegadores, redes e dispositivos, você obtém muito mais informações sobre como essa tecnologia lida com cenários do mundo real e descobre características de desempenho que você pode não ter esperado.

Aqui estão algumas das principais conclusões do estudo da IOWA:

  • Em média, as páginas carregavam um pouco mais rápido quando um service worker estava controlando a página do que sem um service worker, tanto para visitantes novos quanto recorrentes.
  • Visitas a páginas controladas por um service worker carregadas quase instantaneamente para muitos usuários.
  • Os service workers, quando inativos, levavam um tempo para serem inicializados. No entanto, um service worker inativo ainda teve um desempenho melhor do que nenhum service worker.
  • O tempo de inicialização de um service worker inativo foi maior em dispositivos móveis do que em computadores.

Embora os ganhos de desempenho observados em um aplicativo em particular sejam úteis para a comunidade de desenvolvedores em geral, é importante lembrar que esses resultados são específicos ao tipo de site da IOWA (um site de evento) e ao tipo de público que a IOWA tem (principalmente desenvolvedores).

Se você estiver implementando o service worker no seu aplicativo, é importante implementar sua própria estratégia de medição para que você possa avaliar seu próprio desempenho e evitar uma regressão futura. Se sim, compartilhe seus resultados para que todos possam se beneficiar.

Notas de rodapé

  1. Não é totalmente justo comparar o desempenho da implementação do cache do nosso service worker ao desempenho do nosso site apenas com o cache HTTP. Como estávamos otimizando o IOWA para o service worker, não gastamos muito tempo otimizando para o cache HTTP. Se tivéssemos tido, os resultados provavelmente teriam sido diferentes. Para saber mais sobre como otimizar seu site para cache HTTP, leia Como otimizar conteúdo com eficiência.
  2. Dependendo de como seu site carrega seus estilos e conteúdo, é possível que o navegador consiga pintar antes que o conteúdo ou os estilos estejam disponíveis. Nesses casos, firstpaint pode corresponder a uma tela em branco. Se você usar firstpaint, é importante garantir que ele corresponda a um ponto significativo no carregamento dos recursos do site.
  3. Tecnicamente, poderíamos enviar um hit de tempo (que não são interação por padrão) para capturar essas informações em vez de um evento. Na verdade, os hits de velocidade foram adicionados ao Google Analytics especificamente para acompanhar métricas de carregamento como essa. No entanto, os hits de velocidade recebem muitas amostras no momento do processamento, e os valores correspondentes não podem ser usados em segmentos. Devido a essas limitações atuais, os eventos sem interação continuam mais adequados.
  4. Para entender melhor em qual escopo atribuir uma dimensão personalizada no Google Analytics, consulte a seção Dimensão personalizada da Central de Ajuda do Google Analytics. Também é importante entender o modelo de dados do Google Analytics, que consiste em usuários, sessões e interações (hits). Para saber mais, assista a aula da Analytics Academy sobre o modelo de dados do Google Analytics.
  5. Isso não inclui os recursos com carregamento lento após o evento de carregamento.