Um pipeline de 40 minutos não é um detalhe de infraestrutura. Ele muda o comportamento do time inteiro: as pessoas param de rodar o pipeline para validar pequenas mudanças, acumulam alterações em commits grandes, trocam de contexto enquanto esperam e voltam ao trabalho anterior com metade da atenção. Quando o build quebra, a correção demora mais 40 minutos para ser confirmada.
E, em plataforma de CI cobrada por minuto, isso também é uma linha na fatura que cresce com o tamanho do time.
A boa notícia é que a maior parte do tempo perdido está concentrada em poucos lugares previsíveis, e o ganho costuma vir rápido.
Primeiro: meça onde o tempo está
Antes de otimizar qualquer coisa, obtenha a distribuição do tempo por etapa. Todas as plataformas de CI mostram isso, e quase ninguém olha.
O padrão que aparece na maioria dos pipelines lentos:
| Etapa | Fatia típica | Costuma ser |
|---|---|---|
| Instalar dependências | 25–40% | Quase todo eliminável com cache |
| Build da imagem de container | 20–35% | Muito reduzível com camadas e cache |
| Testes | 20–40% | Reduzível com paralelismo e separação por estágio |
| Preparar o ambiente/runner | 5–15% | Reduzível com imagem base própria |
| Deploy | 5–10% | Geralmente já é o menor |
Se você não sabe qual dessas linhas é a sua maior, está prestes a otimizar a errada. Meça antes.
Cache de dependências: o ganho mais barato que existe
Baixar as mesmas bibliotecas a cada execução é puro desperdício. Todas as plataformas de CI têm mecanismo de cache, e configurá-lo é questão de minutos.
O que importa para o cache funcionar de verdade:
- A chave precisa ser o arquivo de lock, não o nome do branch. Enquanto o lock não muda, o cache é reaproveitado — que é exatamente o comportamento desejado.
- Chave de reserva parcial ajuda: se o lock mudou, aproveita-se o cache mais próximo e baixa-se só a diferença.
- Cache do gerenciador de pacotes, não da pasta de módulos instalados. É mais portável e menos propenso a estado inconsistente.
- Verifique se o cache está sendo usado. É comum configurar cache com chave errada e ele nunca acertar — o pipeline continua lento e todo mundo acha que já foi otimizado. O log diz se houve acerto; leia.
Em projetos com muitas dependências, essa única mudança costuma cortar de 5 a 15 minutos.
Build de imagem: camadas na ordem certa
A regra do cache de camada de container é simples e frequentemente violada: uma camada invalida todas as seguintes. Se você copia o código-fonte inteiro antes de instalar as dependências, qualquer alteração em qualquer arquivo invalida a instalação — e ela roda de novo, toda vez.
A ordem correta é sempre a mesma:
- Copiar apenas os arquivos de manifesto e lock.
- Instalar as dependências.
- Copiar o restante do código.
- Compilar.
Assim, mudança de código só invalida da etapa 3 em diante. Dependência só é reinstalada quando o lock muda de verdade.
Some a isso:
- Multi-stage, com o estágio de build separado do de execução. A imagem final leva só o artefato, o que reduz tamanho e tempo de envio ao registry.
- Um arquivo de exclusão (
.dockerignore) bem escrito. Enviar a pasta de módulos, o.gite os artefatos locais para o contexto de build custa tempo em toda execução. - Cache de camadas entre execuções, exportado para o registry ou para o cache da plataforma. Sem isso, runner efêmero começa do zero sempre.
- Imagem base enxuta e fixada por versão. Base menor baixa mais rápido; versão fixa evita rebuild surpresa.
Testes: paralelismo e estágios
Testes costumam ser a maior fatia depois que dependências e build estão resolvidos. Três frentes, em ordem de retorno:
Paralelize. A maioria dos executores de teste divide a suíte em partes que rodam em máquinas simultâneas. Dividir em quatro não corta o tempo por quatro — há sobrecarga de inicialização —, mas corta bastante. É a mudança de maior impacto na etapa de testes.
Separe por estágio. Nem todo teste precisa rodar a cada commit:
- A cada commit: lint, teste unitário, build. Deve terminar em poucos minutos e é o portão que dá retorno rápido a quem programou.
- A cada pull request: integração, contrato, scan de segurança.
- Antes do merge ou em agenda: ponta a ponta, carga, matriz completa de versões.
Isso não reduz o tempo total de teste do projeto; reduz o tempo até o desenvolvedor saber que quebrou, que é a métrica que muda comportamento.
Elimine espera artificial. Teste que dorme por tempo fixo esperando serviço subir é um clássico. Substitua por espera ativa com verificação de saúde: o teste segue assim que o serviço responde, em vez de esperar o pior caso sempre.
E um cuidado: teste instável (que falha aleatoriamente) custa mais que teste lento, porque gera reexecução completa e destrói a confiança no pipeline. Vale rastrear os instáveis e corrigi-los ou isolá-los.
Execute só o que mudou
Em monorepo, rodar o pipeline inteiro quando só um serviço mudou é o maior desperdício isolado que existe.
Duas abordagens:
- Filtro por caminho, nativo nas plataformas de CI: o job de um serviço só dispara se arquivos daquela pasta mudaram. É simples e resolve a maior parte dos casos.
- Ferramenta de build com grafo de dependência, que sabe o que foi afetado pela mudança e executa apenas isso, com cache de resultado. É mais poderoso e exige mais investimento inicial.
Comece pelo filtro por caminho. O ganho aparece no mesmo dia.
O runner: a etapa que ninguém olha
Se cada execução começa instalando as mesmas ferramentas — compilador, CLI de nuvem, utilitários —, você está pagando esse tempo em toda execução.
A correção é construir uma imagem base própria do runner, com tudo já instalado, publicada no seu registry e atualizada por um pipeline separado, semanal. As execuções passam a começar prontas.
Para builds pesados, runner auto-hospedado com disco rápido e cache local persistente muda o patamar — especialmente para compilação e para build de imagem. O contrapeso é que ele deixa de ser efêmero, então precisa de limpeza periódica e de atenção a segurança.
A ordem de ataque
Se tudo estiver ruim ao mesmo tempo, siga esta ordem — ela é decrescente em retorno por esforço:
- Medir a distribuição do tempo por etapa.
- Cache de dependências, e confirmar que ele está acertando.
- Reordenar as camadas do build da imagem e escrever o arquivo de exclusão.
- Paralelizar os testes.
- Separar os testes por estágio, com retorno rápido no commit.
- Filtrar por caminho o que roda.
- Imagem base própria do runner.
- Cache de camadas exportado e, se necessário, runner dedicado.
O número que vale acompanhar
Não é o tempo do pipeline completo. É o tempo do commit até o retorno que diz se quebrou. Esse é o número que altera o comportamento do time — e é ele que deve ficar em um painel visível.
Uma meta realista para começar: retorno em menos de 10 minutos no commit, pipeline completo em menos de 30. Times que chegam lá param de acumular mudança e voltam a integrar com frequência, que era o objetivo do CI desde o começo.
Erros comuns
- Otimizar sem medir, e atacar a etapa que não é a maior.
- Configurar cache com chave errada e nunca verificar se acerta.
- Copiar o código antes de instalar as dependências no build da imagem.
- Não ter arquivo de exclusão, enviando
.gite módulos no contexto de build. - Usar imagem base grande e sem versão fixa.
- Rodar a suíte inteira, inclusive ponta a ponta, a cada commit.
- Espera por tempo fixo em vez de verificação de saúde nos testes.
- Conviver com testes instáveis e reexecutar o pipeline inteiro por causa deles.
- Rodar o pipeline completo do monorepo por mudança em um único serviço.
- Instalar as mesmas ferramentas no runner em toda execução.
- Resolver lentidão comprando runner maior, sem tocar na causa.
Onde a Solvefy/Cloud entra
Otimização de pipeline é um dos trabalhos de retorno mais rápido da nossa consultoria: começamos medindo a distribuição real do tempo, atacamos na ordem de retorno e entregamos o antes e depois em minutos e em custo de execução. Também deixamos o painel do tempo até o primeiro retorno e a documentação das decisões — para o ganho não se perder no trimestre seguinte. Trabalhamos com GitHub Actions, GitLab CI e Jenkins.
Quanto tempo o seu time passa esperando o pipeline? Diagnóstico gratuito, sem compromisso, em solvefy.cloud.
Solvefy/Cloud — Infraestrutura que cresce com você.