O callback que não conseguia dizer o que aconteceu
Dois bugs pequenos e aparentemente sem relação da minha última revisão de código do Greenhouse acabaram compartilhando uma causa raiz que eu já tinha “corrigido” uma vez antes, no mesmo arquivo, alguns dias antes. Essa foi a parte útil desse caso, não a correção em si, mas perceber que a correção da vez passada não tinha realmente terminado o trabalho.
Bug um: importar um projeto fazia ele sumir
O Greenhouse deixa você adotar uma pasta que já existia no disco para dentro do pipeline do app. Você abre um diálogo “Trocar vault”, ele escaneia a pasta que você apontou, e você atribui cada subpasta não reivindicada a uma etapa. Clica em “Importar & Continuar” e o item deveria aparecer na sua lista de trabalho.
Não aparecia, se você estivesse importando para o vault que já estava usando. A importação em si funcionava bem, linha do banco de dados escrita, arquivos onde deveriam estar, mas o dashboard nunca sabia que precisava olhar de novo. Rastreando isso: o componente de seleção tem exatamente duas formas de avisar quem o chamou que algo aconteceu. Uma dispara quando você aponta ele para uma pasta totalmente nova. A outra dispara quando não sobra mais nada para decidir. Importar para a sua pasta atual não é nenhuma das duas. É um evento real sem forma de se anunciar.
Bug dois: eu já tinha escrito a lição para isso
Aqui é onde ficou interessante. Alguns dias antes eu tinha corrigido um bug diferente no exato mesmo componente, e escrito uma nota para mim mesmo depois: quando você extrai um componente que servia um único chamador para algo reusado por dois, audite cada callback em busca de contexto que ele estava assumindo silenciosamente. Aquela correção anterior fez o sinal de “não sobra mais nada para decidir” disparar só quando a situação realmente pedia isso, em vez de disparar errado assim que um diálogo abria.
Aquela correção estava certa. Ela também me deu uma confiança falsa de que o design do componente agora era sólido, quando tudo que eu tinha corrigido de fato era quando um sinal existente disparava. Eu não tinha perguntado se o pequeno conjunto de sinais que ele oferecia conseguia sequer descrever todo evento real que o componente podia produzir. Não conseguia. “Acabei de trocar para uma pasta diferente” e “acabei de importar algo para a pasta em que já estava” são fatos diferentes sobre o mundo, e o componente só tinha espaço para dizer um deles.
A correção dessa vez foi parar de tratar o conjunto de sinais como fixo e adicionar um terceiro, um callback estreito que dispara só quando uma importação de fato aconteceu, ligado diretamente a uma atualização simples de dados. Não roteado pelo handler de “vault trocado”, que reseta uma pilha de outro estado de interface que não tem nada a ver com esse caso. Voltei e adicionei uma seção à minha própria nota sobre a primeira correção, porque o segundo bug não é uma lição nova, é a primeira lição aplicada de forma rasa demais: corrigir quando um callback dispara não te diz se você tem callbacks suficientes para começo de conversa.
Bug três, que não era um bug
A mesma revisão sinalizou uma terceira coisa, e essa eu quase corrigi antes de checar se era real. O Greenhouse tem um pré-visualizador de áudio dentro do app, você clica num arquivo, um diálogo abre, e dá para ouvir sem sair do app. Avançar um projeto move sua pasta no disco, então a teoria era: se você tem o diálogo de preview aberto e depois avança a etapa, o player continua apontando para um caminho de arquivo que não existe mais.
Só que você não consegue fazer isso de verdade. O diálogo de preview usa o elemento nativo <dialog> do navegador, aberto com o método real showModal(), não uma sobreposição feita à mão. Esse método deixa tudo mais na página propriamente inerte, incluindo o botão “Confirmar avanço”. Você fisicamente não consegue clicar nele enquanto um preview está aberto. Você precisa fechar o preview primeiro, o que já limpa o estado dele. O bug para o qual eu estava prestes a escrever código defensivo não podia acontecer, porque a plataforma já tinha descartado essa possibilidade.
Ainda assim eu tinha um bug real e vizinho para corrigir: a lista de arquivos sentada atrás desse diálogo ficava montada o tempo todo em que a view de detalhe de um item estava aberta, e ela só carregava uma vez, no início. Avançar uma etapa movia os arquivos no disco e a lista nunca percebia. Esse só precisou de uma segunda busca junto com a atualização já existente, depois que confirmei que era o caminho realmente alcançável, e não o bloqueado pelo modal.
O que eu levo disso
Duas coisas, e elas puxam em direções um pouco diferentes. Primeiro: corrigir um bug num componente compartilhado é um bom momento para fazer uma segunda pergunta que você não faz automaticamente, não só “isso dispara na hora certa agora”, mas “esse componente consegue, com todo o seu vocabulário de sinais, sequer descrever tudo que ele pode fazer”. A segunda pergunta dá mais trabalho, e é tentador pular ela assim que o bug visível some. Segundo: um relato de bug plausível vale dez minutos checando se o encadeamento que ele descreve é sequer alcançável antes de você escrever uma linha de código defensivo para isso. Às vezes a plataforma já fez a correção por você, e o trabalho de verdade é confirmar isso, não adicionar em cima.
Leitura relacionada
Rescue-or-Keep: construindo a zona de vault para o Greenhouse
Agora arquivar no vault move os arquivos de verdade, e um "Keep Vaulted" que não era um no-op - resetava o timestamp que ordena a revisão diária.
Construindo a tela de detalhes de projeto do Greenhouse
Avançar um estágio conta como um toque, o que acontece no último estágio, e uma tangente de cooldown-vs-rotação que acabou sendo uma pergunta só, não duas.
Construindo o Greenhouse: as primeiras telas
O arco da UI: um assistente construído antes de seus passos existirem, um fluxo de captura removido por estar errado, e o buraco entre o verde e a verdade.
Você também pode achar útil
Proton Mail
E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Pass
Gerenciador de senhas focado em privacidade, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Drive
Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba mais