Blog
Build in Public
26 de março de 20268 min

Sprint de qualidade: corrigir os bugs silenciosos que fazem desinstalar

Um lembrete que desaparece sem aviso, um e-mail que nunca chega, um erro fantasma na inicialização: esses bugs não são manchete em um changelog, mas destroem a confiança dos teus usuários. Em um dia de sprint de qualidade, corrigi quatro problemas silenciosos no TAMSIV — meu gerenciador de tarefas por voz para Android — e a experiência diária mudou radicalmente.

Pontos-chave a reter:
- Bugs silenciosos (lembretes excluídos, e-mails mudos, erros rápidos) causam mais desinstalações do que crashes visíveis.
- Ativar push E e-mail por padrão para lembretes elimina 90% dos tickets "não fui notificado".
- Um sprint de qualidade sem novas funcionalidades é o investimento mais rentável para a retenção de usuários.
- A tradução completa de cada funcionalidade (aqui os emblemas em 6 idiomas) é um sinal de respeito para com os usuários internacionais.
Desenvolvedor trabalhando à noite em frente a telas de código, ambiente de luminária de mesa aconchegante
Os sprints de qualidade geralmente acontecem tarde da noite, quando finalmente podemos nos concentrar nos detalhes que importam.

Por que um sprint de qualidade é mais importante do que uma nova funcionalidade?

Quando tu desenvolves uma aplicação sozinho, a tentação é constante: adicionar funcionalidades. Um novo filtro aqui, uma integração ali. É gratificante, é fácil de mostrar em capturas de tela, faz um belo commit.

Mas a realidade é que os usuários não desinstalam uma aplicação porque falta uma funcionalidade. Eles desinstalam porque um lembrete não funcionou. Porque um e-mail nunca chegou. Porque um erro incompreensível apareceu na inicialização.

De acordo com um estudo da UserTesting, 88% dos usuários nunca mais voltam após uma má experiência. E os bugs silenciosos — aqueles que não fazem a aplicação travar, mas que corroem a confiança — são os mais perigosos. Tu não recebes um relatório de crash. O usuário não relata nada. Ele simplesmente vai embora.

É por isso que decidi dedicar um dia inteiro a um sprint 100% de qualidade. Nenhuma nova funcionalidade. Nenhum refactoring ambicioso. Apenas quatro correções cirúrgicas que, juntas, realmente mudam a experiência diária do TAMSIV.

Como lembretes válidos podem desaparecer silenciosamente?

O cenário: tu crias uma tarefa com três lembretes — um em 10 minutos, um amanhã de manhã, um na sexta-feira. Exceto que o primeiro lembrete já está no passado (tu demoraste muito para validar). O TAMSIV exibia um aviso: "Este lembrete está no passado". Até aí, lógico.

O problema? Ao fechar este aviso, todos os lembretes eram excluídos. Os dois lembretes futuros, perfeitamente válidos, desapareciam junto. Uma limpeza um pouco zelosa demais no código de validação.

Este tipo de bug é particularmente vicioso. O usuário nem sabe que os seus lembretes desapareceram. Ele espera a notificação de amanhã de manhã... que nunca virá. E ele culpará a aplicação — com razão.

A correção técnica

A solução consistiu em separar claramente os lembretes passados dos lembretes futuros na lógica de validação. Concretamente:

  • Filtro temporal: apenas os lembretes cuja data é anterior a Date.now() são marcados como expirados.
  • Aviso direcionado: a mensagem de alerta refere-se apenas aos lembretes que estão efetivamente no passado, com o seu número exato.
  • Conservação garantida: os lembretes futuros permanecem intactos, independentemente do que o usuário faça com o aviso.
  • Fusos horários: aproveitei para reforçar a gestão dos fusos horários na validação — um caso limite que eu tinha subestimado.

O sistema de lembretes e recorrências é um pilar do TAMSIV. Um bug aqui, mesmo mínimo, tem um impacto desproporcional na confiança do usuário.

Por que os e-mails de lembrete nunca chegavam?

O TAMSIV suporta dois canais de notificação para lembretes: o push (notificação no telefone) e o e-mail. Em teoria. Na prática, quando um usuário criava um lembrete, apenas o canal push era ativado por padrão. O e-mail? Desativado. Silenciosamente.

Smartphone exibindo ícones de notificação de e-mail e push em fundo escuro
Push e e-mail: os dois canais devem estar ativos por padrão para cobrir todos os contextos de uso.

Se tu não fosses manualmente marcar "e-mail" nas configurações do lembrete, nunca receberias nada na tua caixa de entrada. Sem erro, sem mensagem — apenas o silêncio. E para um usuário que não consulta sistematicamente o seu telefone, é um lembrete completamente perdido.

Qual é o padrão correto para os canais de notificação?

A questão parece trivial, mas não é. Como o Nielsen Norman Group recomenda em suas pesquisas sobre valores padrão, o padrão deve corresponder ao que a maioria dos usuários espera. E a maioria espera ser notificada — por todos os canais disponíveis.

Agora, os dois canais estão ativos por padrão. Tu recebes um push e um e-mail. Se quiseres desativar um dos dois, ainda é possível, mas o comportamento padrão é o que todos esperam. É o princípio do sane default — um conceito central no design de aplicações de produtividade.

De onde vem este erro "Invalid Refresh Token" na inicialização?

Este é o tipo de bug que não quebra nada, mas mina a confiança. Tu abres a aplicação depois de algumas horas de inatividade, e por uma fração de segundo, um erro "Invalid Refresh Token" pisca na tela. Depois, tudo funciona normalmente.

Imagina: tu abres o TAMSIV de manhã para veres as tuas tarefas do dia, e a primeira coisa que vês é um erro. Tu não sabes o que significa. Tu não sabes se os teus dados estão seguros. Tu não sabes se a aplicação funciona corretamente. Mesmo que tudo esteja perfeitamente OK na realidade, a impressão é desastrosa.

A análise técnica do problema

Na inicialização a frio, o Supabase Auth tentava atualizar o token de autenticação. Se o token tivesse expirado (após algumas horas de inatividade), o SDK levantava um erro antes que o mecanismo de reconexão automática tivesse tempo de fazer o seu trabalho.

O erro subia até a UI, embora não tivesse razão para estar lá — a reconexão sempre acabava por ser bem-sucedida. É um padrão clássico em aplicações que usam tokens JWT com atualização automática: a primeira chamada falha, a atualização é acionada, a segunda chamada é bem-sucedida.

A correção intercepta este erro específico (AuthApiError com o código invalid_refresh_token) no nível certo e o remove da exibição. A atualização da sessão continua a funcionar exatamente como antes, mas o usuário não vê mais uma mensagem ansiogênica que não lhe diz respeito.

Este tipo de gestão de erros é crítico para qualquer aplicação que use a autenticação Supabase. O SDK faz bem o seu trabalho — só é preciso evitar expor os seus erros intermediários ao usuário.

Como traduzir um sistema de gamificação em 6 idiomas?

O TAMSIV está disponível em francês, inglês, alemão, espanhol, italiano e português. O sistema de gamificação — níveis, emblemas, sequências — faz parte das funcionalidades que os usuários descobrem progressivamente. Exceto que o guia explicativo dos emblemas existia apenas em francês e inglês.

Globo rodeado por bandeiras e símbolos representando seis idiomas diferentes
Traduzir cada elemento da interface é um investimento que compensa a longo prazo.

É um problema mais sutil do que parece. Um usuário de língua alemã que descobre o sistema de emblemas e se depara com um texto em francês ou inglês sentirá imediatamente que esta parte da aplicação não está terminada. É um sinal negativo muito forte, especialmente quando o resto da interface está corretamente traduzido.

O processo de internacionalização dos emblemas

Os 10 emblemas do TAMSIV têm cada um um nome, uma descrição e condições de desbloqueio. Isso significa 30 cadeias de texto para traduzir em 4 idiomas adicionais (alemão, espanhol, italiano, português). O sistema de internacionalização em 6 idiomas que implementei gere isso com um pipeline de tradução automática via OpenRouter, seguido de uma revisão manual para os termos de gamificação.

Não é uma correção espetacular, mas é o tipo de detalhe que faz com que um usuário lusófono ou germanófono se sinta em casa na aplicação. E como explica a CSA Research, 76% dos consumidores preferem comprar produtos no seu próprio idioma.

Qual é o impacto real dessas correções na retenção?

Estas quatro correções representam um dia de trabalho. Nenhuma delas teria sido um título de changelog emocionante. Mas juntas, elas eliminam atritos reais:

  • Lembretes perdidos: nenhum lembrete válido desaparece mais após um aviso sobre um lembrete expirado.
  • E-mails perdidos: os dois canais de notificação estão ativos por padrão, sem mais surpresas.
  • Erro na inicialização: a mensagem "Invalid Refresh Token" nunca mais é exibida.
  • Gamificação traduzida: os 10 emblemas estão disponíveis nos 6 idiomas suportados.

Em termos de retenção, cada um desses bugs era um ponto de atrito invisível. O usuário não relata "o teu e-mail de lembrete não chegou" — ele simplesmente pensa que a aplicação não funciona e a desinstala. De acordo com a Adjust, a taxa de retenção média de uma aplicação no dia 30 é de 6%. Cada atrito eliminado empurra esse número para cima.

Como organizar um sprint de qualidade quando se é um desenvolvedor solo?

Ser desenvolvedor solo em um projeto como o TAMSIV significa que ninguém vai planear um sprint de qualidade por ti. Aqui está o método que eu uso:

  1. Usar a própria aplicação diariamente — o dogfooding é a melhor fonte de bugs. Cada irritação anotada em um memo de voz (com o TAMSIV, obviamente).
  2. Priorizar por impacto no usuário — um bug que afeta 100% dos usuários a cada inicialização tem prioridade sobre um bug que afeta 2% dos casos.
  3. Limitar o escopo — um dia, no máximo 4 correções. Sem refactoring tentador no meio do caminho.
  4. Testar em dispositivos reais — os 12 beta-testers estão lá para isso. Um bug reproduzido em 3 dispositivos diferentes é um bug real.

O sprint zero-feature que fiz na semana seguinte confirmou esta abordagem: o feedback dos beta-testers foi muito melhor após estas correções silenciosas do que após a adição de novas funcionalidades.

Perguntas frequentes

Quanto tempo é necessário para um sprint de qualidade eficaz?

Um dia concentrado é suficiente para 3 a 5 correções direcionadas. O importante é não misturar com o desenvolvimento de funcionalidades — o contexto mental é diferente. Recomendo um sprint de qualidade a cada 2 semanas para um projeto em fase de lançamento.

Como detetar bugs silenciosos quando não há relatório de crash?

O dogfooding diário é o método número um. Usa a tua própria aplicação como um usuário normal, não como um desenvolvedor. Ferramentas como o Firebase Analytics também ajudam a detetar padrões anormais (sessões muito curtas, funcionalidades nunca usadas).

Por que ativar push E e-mail por padrão em vez de deixar o usuário escolher?

Porque a maioria dos usuários nunca muda as configurações padrão (pesquisas do Nielsen Norman Group). O opt-out é mais respeitoso do que o opt-in para as notificações de lembrete — o usuário pediu explicitamente para ser lembrado, ele espera receber a notificação em todos os canais disponíveis.

O erro "Invalid Refresh Token" é perigoso para os dados?

Não, absolutamente não. É um erro intermediário normal no fluxo de atualização do token JWT. O Supabase Auth gere isso automaticamente — o token expirado é substituído por um novo em segundo plano. O problema era apenas a exibição desse erro técnico ao usuário.

Como gerir a tradução de termos de gamificação (emblemas, níveis)?

Eu uso um pipeline de tradução automática via OpenRouter, seguido de uma revisão para os termos específicos de jogos. Alguns termos como "streak" ou "badge" são frequentemente mantidos em inglês mesmo em versões localizadas, pois são universalmente compreendidos pelos usuários de aplicações móveis.