Como reduzir tempo de build e evitar artefatos duplicados em CI para monólitos PHP grandes
Por Kevin Marcondes · Publicado em · 6 min de leitura
Seu pipeline está lento? Veja como reduzir tempo de build e evitar artefatos duplicados em CI para monólitos PHP grandes. O caminho passa por cache eficiente do Composer, camadas Docker determinísticas e um artefato único para alimentar testes e deploy. Abaixo, um plano prático para diagnosticar gargalos e aplicar melhorias sem refazer tudo do zero.
Sintomas e causas comuns em builds de monólitos PHP
Monólitos PHP grandes sofrem com tempo de checkout, instalação de dependências e reconstrução repetida de artefatos. Em muitos pipelines, cada job recompila tudo, gerando artefatos idênticos várias vezes e desperdiçando cache.
Mapeie rapidamente o que mais consome tempo. Quase sempre é Composer, build de assets e download de camadas Docker. Corrigindo esses pontos, o ganho costuma ser imediato.
- Instalações repetidas do Composer em jobs paralelos.
- Cache inexistente ou inválido a cada commit.
- Dockerfile que invalida camadas cedo demais.
- Artefato produzido em mais de um job da pipeline.
- Falta de concorrência controlada e cancelamento de builds antigos.
| Sintoma | Causa provável | Medida prática |
|---|---|---|
| composer install demora em todo job | cache do Composer não persistido ou chaveada errado | persistir $COMPOSER_CACHE_DIR por chave do composer.lock |
| Docker rebuilda tudo a cada commit | ordem de COPY inválida; sem BuildKit cache remoto | reordenar Dockerfile e ativar cache-from/cache-to |
| artefatos em duplicidade | “build” acontece em múltiplos stages | criar artefato único e fazer fan-out |
| checkout pesado | repositório gigante; histórico completo | shallow/partial clone e limpeza de blobs de mídia |
Diagnóstico orientado a dados do pipeline
Antes de mudar, meça. Anote durações por etapa e identifique o que varia entre commits. Foque nos 2 ou 3 maiores blocos de tempo e no que é repetido sem necessidade.
Verifique também I/O do runner. Discos lentos e armazenamento de cache em rede de alta latência sabotam pipelines mesmo bem configuradas.
- Habilite logs detalhados do Composer para ver cache hits/misses.
- Revise o tempo de “checkout” e habilite shallow clone.
- Separe o tempo de build de imagens do tempo de testes.
- Meça cache hit rate por chave (commit, composer.lock).
Composer rápido e determinístico (sem retrabalho)
O maior ganho vem de tratar dependências de forma determinística e cacheável. Use sempre o composer.lock e flags não interativas. Evite reinstalar vendor/ em vários jobs: produza-o uma vez e reutilize.
Persista o diretório de cache do Composer entre jobs. Chaveie o cache pelo hash do composer.lock para invalidar apenas quando dependências mudarem.
- Use --prefer-dist, --no-interaction e autoloader otimizado.
- Cacheie $COMPOSER_CACHE_DIR, não o vendor/ cru entre branches.
- Gere o vendor/ uma única vez e empacote no artefato final.
- Tokens de acesso para repositórios privados devem ficar em secrets.
Se você usa containers: BuildKit, camadas e cache remoto
Com Docker, organize o Dockerfile para maximizar cache: copie composer.json e composer.lock antes do código. Instale dependências primeiro. Só depois copie o restante. Ative BuildKit e cache remoto para compartilhar camadas entre runners.
Para detalhes práticos de cache remoto e otimizações com BuildKit, veja o guia específico abaixo.
- Multi-stage para separar build e runtime.
- COPY em ordem: arquivos de dependência antes do código.
- --mount=type=cache no Composer dentro do build.
- cache-to/cache-from para reusar camadas entre builds.
Artefato único: build once, fan-out para testes e deploy
Evite artefatos duplicados produzindo um único pacote versionado por commit (ex.: zip/tar com código, vendor/ e assets). Jobs seguintes apenas consomem esse artefato. Isso reduz variação entre ambientes e elimina retrabalho.
Nomeie o artefato com o SHA do commit e opcionalmente um content hash para permitir deduplicação no armazenamento.
- Gere o artefato após lint + unit mais rápidos.
- Teste integração e e2e usando o mesmo artefato.
- Deploy consome exatamente o artefato testado.
- Defina tempo de retenção e política de limpeza.
Estratégia de pipeline: ordem, matrizes e concorrência
Estruture a pipeline em estágios claros: checkout leve, build do artefato, fan-out de testes, publicação e deploy. Use matrizes para paralelizar testes, nunca o build do artefato.
Controle concorrência: cancele runs obsoletos do mesmo branch e limite builds simultâneos que disputam caches. Assim, você evita filas longas e duplicação desnecessária.
- Gate: só crie artefato se checagens rápidas passarem.
- Matrix só para testes; build do artefato é único.
- Convergir para uma etapa de publicação/deploy.
- Definir regras de cancelamento de runs antigos.
Higiene de cache, retenção e segurança
Cache acelera, mas sem higiene vira entulho. Limpe caches ao remover branches. Renove chaves quando composer.lock muda. Monitore o hit rate e ajuste o particionamento.
Tenha atenção a secrets e provedores privados de pacotes. Armazene tokens de forma segura e restrita ao escopo da pipeline.
- Defina TTLs diferentes para branches e main.
- Evite compartilhar vendor/ entre branches distintos.
- Rotacione tokens periodicamente e isole permissões.
- Audite quem acessa artefatos e logs sensíveis.
Perguntas frequentes
Como evitar reinstalar o vendor/ em cada job do CI?
Gere o vendor/ uma única vez na etapa de build e empacote no artefato final. Os demais jobs consomem esse artefato. Para velocidade adicional, persista o cache do Composer chaveado pelo hash do composer.lock.
BuildKit ajuda mesmo em projetos PHP sem muito C/C++?
Sim. Mesmo sem compilações pesadas, camadas determinísticas e cache remoto reduzem downloads e reinstalações. A ordem de COPY correta e o reuso de camadas do Composer costumam render ganhos consideráveis.
Qual é a melhor chave para cache do Composer no CI?
Use uma chave derivada do hash do composer.lock e do sistema operacional/versão do PHP. Assim, o cache só é invalidado quando as dependências realmente mudam, evitando quebras por plataforma diferente.
Como eliminar artefatos duplicados em pipelines paralelas?
Centralize o build do artefato em um único job por commit e use esse pacote em fan-out. Nomeie com SHA do commit e, se possível, um content hash para deduplicação no storage. Cancele execuções antigas do mesmo branch.
Devo cachear o diretório vendor/ diretamente?
Prefira cachear o diretório de cache do Composer e produzir o vendor/ no artefato único. Cachear vendor/ entre branches pode introduzir inconsistências difíceis de diagnosticar e piorar a depuração.
Quer apoio para desenhar seu pipeline?
Se você precisa revisar o CI do seu monólito PHP e implementar artefato único, cache eficiente e BuildKit, podemos conversar sobre o seu contexto e prioridades.
Falar sobre otimização de CIVeja também
- 502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM: como diagnosticar e corrigir timeouts e excesso de conexões
- Como diagnosticar e capturar pacotes para intermitência de rede entre containers Docker no Debian
- Guia prático: como reduzir uso de disco por logs de containers Docker em VPS com rotação, compressão e retenção sem perder rastreabilidade
- Como detectar e corrigir inconsistências de SKU duplicados entre WooCommerce e ERP sem perder pedidos