GitHub Actions em monorepos: erros que geram falhas, lentidão ou retrabalho
Seu CI executa projetos demais, ignora dependências ou bloqueia merges? Conheça dez erros de GitHub Actions em monorepos e como verificar filtros, checks, cache, matrizes e permissões.
Max Alex

Neste artigo (10 tópicos)
GitHub Actions em monorepos: como identificar gargalos, corrigir a seleção de projetos afetados e tornar o CI mais rápido e confiável.
Um pull request altera apenas o frontend, mas dispara testes de todas as aplicações. Outro modifica uma biblioteca compartilhada e recebe aprovação sem validar seus consumidores. Nos dois casos, o problema está na forma como o pipeline decide o que executar.
Usar GitHub Actions em monorepos exige conectar mudanças, dependências e regras de merge. A otimização precisa reduzir trabalho desnecessário e preservar a cobertura de validação.
Considere um repositório ilustrativo com apps/web, apps/api e packages/shared. Web e API consomem a biblioteca compartilhada. Esse exemplo ajuda a reconhecer os dez erros abaixo e verificar suas correções.
1. Executar todos os projetos em qualquer alteração
O sintoma aparece quando mudanças pequenas enfrentam a mesma fila e o mesmo conjunto de testes de uma alteração estrutural. A causa costuma ser uma regra simples demais: qualquer commit executa tudo.
Projeto alterado e projeto afetado são conceitos diferentes. O primeiro teve arquivos modificados. O segundo precisa de validação porque a mudança pode alterar seu comportamento, mesmo sem modificações em seus próprios arquivos.
Uma alteração isolada em um componente de apps/web pode exigir apenas as verificações do frontend. Uma mudança em packages/shared precisa considerar os testes da biblioteca, da web e da API, conforme as dependências existentes.
Para corrigir, mantenha um mapa de dependências ou use uma ferramenta que represente esse grafo. Inclua dependências transitivas e trate configurações globais, scripts comuns e mudanças no processo de build como possíveis motivos para ampliar a validação.
Verifique a seleção com alterações representativas. Se o detector encontrar um caminho desconhecido ou não conseguir calcular o impacto, execute uma validação abrangente ou interrompa o pipeline com uma falha explícita. Uma seleção vazia não deve ser a resposta automática para um erro.
2. Confiar apenas em paths e ignorar dependências compartilhadas
Os filtros de caminhos no GitHub Actions ajudam a decidir se um workflow começa. Já a seleção de jobs, depois da inicialização, determina quais validações serão executadas. Essas decisões têm responsabilidades diferentes.
Um filtro que observa apenas apps/api/** não representa a dependência da API em packages/shared. Acrescentar a biblioteca ao conjunto analisado resolve esse caso, mas também é necessário considerar lockfiles, scripts de build e configurações da raiz.
Em regras manuais, registre os caminhos que afetam cada projeto. Quando as relações ficarem difíceis de revisar, adote um grafo de dependências. O guia sobre como implementar GitHub Actions em monorepos complementa esse desenho.
A comparação também depende do evento: pull requests usam uma comparação de três pontos; pushes em branches existentes usam dois pontos. Padrões negativos dependem da ordem, e os filtros nativos analisam uma lista limitada a 300 arquivos. Não trate esse mecanismo como prova completa de impacto em mudanças amplas.
Valide com pull requests que alterem uma biblioteca, removam arquivos e renomeiem diretórios. Confira a lista selecionada e o motivo de cada inclusão. Consulte a documentação oficial de sintaxe antes de ajustar os padrões.
3. Deixar checks obrigatórios pendentes ou aprovar validações incompletas
Um workflow omitido por filtros pode deixar checks obrigatórios pendentes. Um job ignorado dentro de uma execução tem outro comportamento: pode apresentar resultado de sucesso para fins de proteção de branch. Essa diferença exige cuidado com a aprovação final.
Use um check agregador com nome estável, em um workflow que inicie nos eventos exigidos pela política de merge. Confira se esse nome corresponde à regra de proteção. Se houver fila de merge, contemple merge_group.
O agregador deve depender do detector e das validações, com uma condição como if: ${{ always() }} para avaliar os resultados mesmo após falhas. Sua lógica precisa exigir sucesso do detector e dos jobs selecionados, rejeitar falhas e cancelamentos e aceitar jobs ignorados somente quando a seleção justificar essa ausência.
Defina um contrato verificável para o detector: resultado válido, lista de projetos e indicação explícita de que existem validações aplicáveis. Saída ausente, JSON inválido ou erro de execução devem reprovar o check final.
Teste três situações: documentação sem validações aplicáveis, falha nos testes da API e falha no detector. Apenas a primeira pode ser aprovada sem testes de aplicação, conforme a política documentada do repositório.
4. Restaurar cache com chaves e caminhos inadequados
O cache parece funcionar nos logs, mas a instalação continua lenta ou o build reaproveita resultados incompatíveis. Antes de mudar a chave, identifique o conteúdo armazenado.
- Cache de dependências: reaproveita arquivos do gerenciador de pacotes e pode reduzir downloads.
- Cache de resultados: reaproveita saídas de tarefas quando suas entradas relevantes permanecem equivalentes.
- Artefatos: transportam e preservam arquivos produzidos pela execução, como um build consumido por outro job.
As dimensões de compatibilidade dependem desse conteúdo. Sistema operacional, arquitetura, runtime, gerenciador e lockfile podem participar da chave. Para resultados de tarefas, considere também código, configurações e variáveis que alteram a saída.
Em um monorepo com lockfile na raiz, procurar apps/web/package-lock.json quando esse arquivo não existe compromete a configuração. Aponte para o lockfile real e para o armazenamento utilizado pelo gerenciador. Se os workspaces compartilham esse armazenamento, criar um cache separado por aplicação pode apenas duplicar dados.
Restaurar o cache do gerenciador não elimina necessariamente a instalação. Preserve o comando reprodutível adequado à stack. Para enviar o build da web a outro job, publique um artefato identificado pela execução, em vez de depender de um cache incidental.
Compare execuções com cache frio e aquecido. Registre tempo de restauração, instalação, salvamento e trabalho evitado. Uma taxa alta de acerto só ajuda quando reduz o custo total.
5. Gerar matrizes grandes e paralelismo sem controle
Uma matriz multiplica as combinações configuradas. Em um cenário hipotético, 12 projetos em três versões de runtime geram 36 combinações. Acrescentar sistemas operacionais amplia esse conjunto novamente.
Faça a seleção de projetos afetados antes de expandir a matriz. Se apenas dois projetos precisam de validação e os três runtimes continuam obrigatórios, restam seis combinações. Reduzir também os runtimes exige uma decisão sobre compatibilidade suportada.
Uma matriz dinâmica no GitHub Actions pode consumir uma saída JSON do detector por meio de fromJSON. Valide essa saída e trate explicitamente o caso sem projetos: condicione o job antes da expansão, sem depender de uma matriz vazia como controle de fluxo.
Use max-parallel para limitar a quantidade de jobs simultâneos da matriz. Ajuste esse número à disponibilidade de runners e à capacidade dos serviços usados nos testes.
Defina fail-fast conforme o diagnóstico necessário. Interromper combinações após uma falha economiza trabalho; continuar pode revelar problemas independentes. Compare duração, tempo de fila e conjunto de falhas encontrado antes de escolher.
6. Duplicar execuções e cancelar trabalho com concurrency mal definida
Dois pipelines para o mesmo commit podem surgir da sobreposição entre push e pull_request. Revise quais branches precisam de validação por push e quais mudanças já são cobertas pelo fluxo de pull requests.
Uma organização possível é validar pull requests e manter o push para a branch principal. Ajuste essa política caso também seja necessário validar branches sem pull request.
Depois, defina a unidade de isolamento da concorrência. Para validações, um grupo pode combinar workflow e número do pull request, usando a referência como alternativa em outros eventos:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: trueEsse trecho é adequado quando uma nova execução torna a validação anterior dispensável. Confirme seu comportamento com dois pushes consecutivos no mesmo pull request e com pull requests diferentes.
Entregas precisam de uma política própria. Deploys independentes de API e web podem usar grupos por projeto e ambiente. Avalie se interromper uma implantação em andamento é seguro antes de habilitar cancelamento. O agrupamento deve refletir os recursos que realmente precisam de coordenação.
7. Assumir diretórios, histórico Git e ambientes disponíveis
Um comando funciona localmente, mas falha no CI porque começou no diretório errado. Outro não calcula os projetos afetados porque a referência base está ausente. São falhas de preparação do ambiente que podem parecer problemas na aplicação.
Defina o diretório de execução de cada comando. Em uma API com configuração local, use working-directory: apps/api. Se o gerenciador exige instalação na raiz dos workspaces, mantenha essa etapa na raiz e selecione o projeto pelo mecanismo apropriado.
O checkout busca, por padrão, um único commit. Um detector que depende do histórico precisa de uma busca adicional ou de configuração suficiente para disponibilizar as referências usadas na comparação. Buscar todo o histórico com fetch-depth: 0 é uma opção, cujo custo deve ser avaliado.
Não substitua uma referência ausente por uma comparação arbitrária apenas para continuar. Confirme a disponibilidade dos commits necessários e aplique a estratégia conservadora caso o cálculo seja impossível.
Também não presuma que um job encontrará arquivos produzidos por outro. Prepare suas dependências e transfira as saídas necessárias por artefatos. Em runners próprios, arquivos residuais exigem limpeza e isolamento.
Fixe versões de runtime e gerenciador e use instalação baseada no lockfile. Para verificar a correção, execute a validação em um ambiente limpo e confirme que ela não depende de arquivos locais ou de uma execução anterior.
8. Copiar workflows e deixar as configurações divergirem
Quando cada aplicação mantém uma cópia do mesmo pipeline, uma atualização pode chegar à web e deixar a API para trás. Com o tempo, permissões, versões e etapas comuns passam a divergir.
Workflows reutilizáveis permitem compartilhar a definição de jobs e sua orquestração. Composite actions agrupam uma sequência de passos executada dentro de um job do workflow consumidor.
Escolha conforme o escopo. Se web e API compartilham preparação e instalação, uma composite action pode concentrar esses passos. Se compartilham também a estrutura de jobs, um workflow reutilizável pode representar melhor o contrato.
Mantenha diferenças necessárias explícitas. Comandos de teste, diretórios e requisitos de build podem ser entradas ou permanecer próximos da configuração de cada projeto. Evite uma abstração com tantas condicionais que o leitor precise simular mentalmente todo o pipeline.
Documente entradas, saídas, segredos necessários e permissões. Ao alterar um componente compartilhado, valide consumidores representativos, inclusive os que usam opções diferentes. Reutilização reduz cópias, mas aumenta o alcance de uma mudança comum.
9. Misturar testes de contribuições externas com credenciais de entrega
Um pipeline pode funcionar em branches internas e falhar em contribuições de forks porque o acesso a segredos e permissões é diferente. O desenho precisa permitir a validação sem depender de credenciais de produção.
Defina permissões mínimas por job e disponibilize credenciais de publicação apenas nas operações autorizadas. Vincule a entrega aos eventos e às regras do ambiente apropriado.
Não use pull_request_target como atalho para executar código de um pull request com acesso privilegiado. O risco envolve também scripts, dependências e arquivos produzidos por código não confiável. Uma automação que precisa desse evento deve preservar a separação entre dados externos e execução privilegiada.
Para actions de terceiros, avalie referências por SHA completo e estabeleça um processo de atualização e revisão. A referência fixa torna a versão executada identificável, mas sua manutenção continua necessária.
Verifique o fluxo com uma contribuição de fork: os testes devem funcionar sem credenciais de entrega, e nenhum caminho dessa validação deve conseguir publicar. Confira também se um evento autorizado consegue entregar apenas no ambiente previsto.
10. Otimizar sem medir e sem testar a seleção de jobs
Reduzir o número de jobs não comprova que o CI melhorou. A seleção pode ter removido cobertura, e um pipeline mais paralelo pode terminar antes enquanto consome mais recursos.
Separe três medidas: tempo de fila, entre a disponibilidade do job e o início da execução; duração da validação, até o resultado final; e consumo agregado, considerando o trabalho dos jobs. Cada medida aponta para causas diferentes.
Acompanhe mediana e p95 de execuções comparáveis, além das tarefas mais lentas, do comportamento do cache e das reexecuções manuais. No diagnóstico de testes instáveis, registre a falha original: uma reexecução bem-sucedida não explica sua causa.
Use uma tabela de verificação para os projetos afetados no monorepo. O exemplo abaixo pressupõe que web e API consomem shared e que a política permite dispensar testes de aplicação em mudanças exclusivamente documentais.
| Mudança | Validações esperadas | Execução observada | Check obrigatório |
|---|---|---|---|
| Componente isolado em web | Web | Registrar jobs executados | Aprovar apenas com sucesso de web e detector |
| Código isolado da API | API | Registrar jobs executados | Aprovar apenas com sucesso da API e detector |
| Biblioteca shared | Shared, web e API | Registrar jobs executados | Exigir sucesso de todas as validações selecionadas |
| Lockfile ou configuração global | Conjunto abrangente definido pela política | Registrar jobs executados | Exigir sucesso do conjunto selecionado |
| Somente documentação | Detector e verificações documentais aplicáveis | Confirmar ausência justificada de testes de aplicação | Aceitar somente seleção válida e verificações aplicáveis bem-sucedidas |
| Falha no detector | Falha explícita ou validação abrangente de contingência | Registrar o caminho adotado | Nunca aprovar por uma seleção vazia acidental |
Priorize mudanças relevantes sem validação e checks que aprovam incorretamente. Depois investigue duplicações, filas, instalações e tarefas lentas. Compare antes e depois com tipos de mudança e condições semelhantes, registrando as diferenças de infraestrutura.
Escolha o gargalo que mais atrasa os merges, registre o comportamento atual e valide uma correção com mudanças representativas do monorepo. Para aprofundar o diagnóstico, explore os conteúdos de DevOps & CI/CD do blog Max Alex.
Max Alex
Desenvolvedor full stack especializado em SaaS, Laravel, IA aplicada, WhatsApp API, automações e integrações. Criação de sistemas web escaláveis para empresas.
Continue lendo

Como implementar GitHub Actions em monorepos: guia prático para produção
Aprenda a implementar GitHub Actions em monorepos com execução seletiva por caminhos, workflows reutilizáveis, cache, matriz de jobs, segurança e promoção de artefatos para produção.

OpenTelemetry em microserviços: erros que geram falhas, lentidão ou retrabalho
Traces incompletos, overhead e perda de telemetria podem ter causas diferentes. Entenda oito erros de OpenTelemetry em microserviços e use um checklist para diagnosticar, corrigir e validar a instrumentação em produção.

reverse proxy em APIs backend: erros que geram falhas, lentidão ou retrabalho
Erros de rotas, headers, timeouts e conexões no Nginx podem causar falhas e lentidão em APIs. Veja como localizar a camada responsável, corrigir a configuração e validar o resultado antes de publicar.