DEV Community

Cover image for Código morto em Flutter é mais do que um warning do analyzer
Lucas Menezes
Lucas Menezes

Posted on

Código morto em Flutter é mais do que um warning do analyzer

Encontrar um warning unused_element é fácil. Decidir se um arquivo pode ser apagado com segurança não é.

Aplicações Flutter possuem várias formas legítimas de usar código sem uma referência direta óbvia: registries gerados, tabelas de rotas, injeção de dependências, imports condicionais, flavors, platform channels, annotations, helpers exclusivos de testes e exports públicos de packages. Uma busca que retorna zero referências é uma evidência útil, mas não é um veredito.

Essa diferença me levou a criar a flutter-dead-code-audit, uma Agent Skill open source para auditar declarações Dart sem uso e arquivos .dart potencialmente órfãos por meio de um fluxo que exige revisão antes da remoção.

Demonstração da flutter-dead-code-audit

▶️ Assista à demonstração curta

Repositório: github.com/LucasMenezeslc/flutter-dead-code-audit

O analyzer responde apenas parte da pergunta

O analyzer do Dart é excelente para encontrar problemas locais como:

  • imports sem uso;
  • campos e métodos privados sem uso;
  • branches inalcançáveis;
  • variáveis locais sem uso;
  • expressões redundantes.

Mas uma limpeza de repositório também precisa responder perguntas mais amplas:

  • O arquivo é alcançável por algum entrypoint suportado?
  • O símbolo chega ao consumidor por um barrel export?
  • Ele é registrado por injeção de dependências?
  • É carregado apenas em uma plataforma ou flavor?
  • Faz parte de uma API pública consumida fora do repositório?
  • É gerado ou pertence a um generator?
  • Foi mantido intencionalmente por compatibilidade ou rollback?

Uma ferramenta que ignora essas perguntas pode produzir um relatório convincente e ainda recomendar a remoção errada.

Estado write-only é um bom exemplo

Considere um campo que recebe valores em diferentes callbacks, mas nunca é lido:

bool _hasPlayed = false;

void onStarted() {
  _hasPlayed = true;
}

void onStopped() {
  _hasPlayed = false;
}
Enter fullscreen mode Exit fullscreen mode

Mecanicamente, o campo é write-only. Remover apenas a declaração não basta; todas as atribuições também precisam ser removidas para o código continuar compilando.

Existe, porém, uma pergunta de produto mais importante: o campo foi abandonado ou faltou uma leitura que deveria controlar replay, analytics, persistência ou estado visual?

Portanto, a conclusão correta não é automaticamente “cleanup seguro”. Ela pode ser:

O campo é write-only no código atual, mas a intenção de produto ainda não foi resolvida.

Por isso o fluxo separa evidência mecânica de decisão humana.

Um fluxo orientado à revisão

A skill trabalha em quatro etapas:

1. Audit

O modo Audit é somente leitura. Os scripts combinam:

  1. diagnósticos do analyzer em formato legível por máquina;
  2. reachability determinística de imports, exports e arquivos part;
  3. buscas de símbolos e strings em todo o repositório;
  4. verificações de rotas, DI, codegen, plataformas, flavors, testes e APIs públicas.

O resultado é uma lista de candidatos, não uma lista automática de exclusão.

2. Review

Os candidatos são apresentados em grupos pequenos. Cada um recebe uma decisão:

  • remove — evidência e contexto sustentam a remoção;
  • retain — manter intencionalmente e registrar o motivo;
  • defer — ainda faltam evidências ou contexto do time.

Código mantido continua visível em relatórios futuros. Ele não é silenciosamente ignorado, evitando que a mesma investigação seja repetida alguns meses depois.

3. Cleanup

O Cleanup só pode alterar candidatos explicitamente aprovados como remove. As mudanças são aplicadas em lotes pequenos e seguidas por analyzer e testes.

4. Tests

Depois da limpeza, o relatório sugere verificações de comportamento com base na área afetada. A remoção de um campo de playback, por exemplo, deve gerar um checklist do fluxo de reprodução — não apenas um analyzer verde.

Correção estática é necessária, mas não prova que o comportamento suportado foi preservado.

Evidência em vez de confiança aparente

Um candidato de alta confiança normalmente precisa de pelo menos dois sinais independentes e de verificações de falso positivo concluídas.

A hierarquia de evidências utilizada é:

  1. diagnóstico do analyzer ou compilador;
  2. reachability por imports, exports e part;
  3. buscas de símbolos e strings no repositório;
  4. inspeção de registros em runtime e mecanismos do framework;
  5. testes ou builds após uma remoção aprovada.

Quando os sinais entram em conflito, a confiança diminui. O fluxo não esconde o conflito atrás de uma média.

Relatórios feitos para decidir

Listas enormes são difíceis de revisar. Por isso, os resultados são agrupados em:

  • Ready for review — evidência forte e nenhum consumidor dinâmico plausível;
  • Review carefully — provável código morto em uma fronteira sensível;
  • Needs team context — a intenção não pode ser determinada pelo repositório;
  • Potential logic issue — pode revelar um bug em vez de cleanup;
  • Intentionally retained — mantido com motivo documentado;
  • Not dead / excluded — investigado e confirmado como ativo ou protegido.

Cada candidato preserva localização, evidências, verificações, confiança, risco, recomendação e comando de validação. A visão executiva permanece compacta, enquanto os detalhes ficam disponíveis quando necessários.

Instalação

Na raiz de um projeto Flutter ou Dart:

npx skills add LucasMenezeslc/flutter-dead-code-audit
Enter fullscreen mode Exit fullscreen mode

Depois, comece com uma solicitação somente leitura:

Use $flutter-dead-code-audit em modo Audit neste projeto.
Não modifique arquivos. Gere um relatório e apresente o primeiro grupo para revisão.
Enter fullscreen mode Exit fullscreen mode

Os scripts exigem Python 3.10 ou superior e utilizam apenas a biblioteca padrão. O Dart pode vir do PATH ou do FVM. A saída utiliza caminhos relativos para não expor o diretório absoluto do projeto nos relatórios.

Limites atuais

A skill ainda não audita:

  • código duplicado;
  • assets ou traduções sem uso;
  • dependências sem uso no pubspec.yaml;
  • performance ou segurança da aplicação.

Ela também não instala ferramentas no projeto, não altera regras do analyzer e não remove arquivos gerados diretamente durante uma auditoria.

Sugestões e contribuições são bem-vindas

O projeto usa licença MIT e está aberto a contribuições. O feedback mais útil é um exemplo Flutter ou Dart pequeno e reproduzível que revele um falso positivo, um falso negativo ou uma forma de uso dinâmico ainda não modelada.

Se você já encontrou código aparentemente morto escondido por rotas, injeção de dependências, codegen, imports condicionais, implementações de plataforma ou exports públicos, quero conhecer esse caso.

Repositório: flutter-dead-code-audit

Top comments (0)