O GitHub Actions está matando equipes de engenharia aos poucos
(iankduncan.com)- Embora seja amplamente usado por ser um CI incluído por padrão no repositório, sua ineficiência estrutural e experiência de usuário instável reduzem a produtividade dos desenvolvedores
- O carregamento lento do visualizador de logs e os travamentos do navegador, além da sintaxe complexa de YAML e erros de expressões, levam a depuração repetitiva
- Por conta de uma arquitetura em que você não possui os recursos de computação, a ferramenta expõe limitações em desempenho, escalabilidade e controle do ambiente
- Ao tentar contornar vários problemas, repete-se a situação de recriar o próprio CI com YAML complexo ou scripts Bash gigantescos
- Em comparação, o Buildkite oferece uma alternativa de CI sustentável no longo prazo com estrutura YAML simples, agentes com possibilidade de self-hosting e uma experiência prática de logs
Problemas do GitHub Actions
- O visualizador de logs do GitHub Actions é ineficiente: até para verificar um erro simples são necessários vários cliques e carregamentos de página
- Quando um build falha, é preciso passar pela página de resumo dos checks → página de execução do workflow → página do job → clique na etapa recolhida, exigindo 3 a 4 transições de página, cada uma com seu próprio carregamento
- Em logs de builds grandes, o navegador trava repetidamente, e o congelamento do Chrome ao usar a busca é reproduzível
- Em logs longos, a rolagem simplesmente deixa de funcionar, e no fim você precisa baixar o artefato do log bruto e abrir em um editor de texto
- O botão de voltar leva não para a página original do PR, mas para páginas imprevisíveis da UI do GitHub Actions, enchendo o histórico do navegador com URLs do Actions
- A improdutividade do processo de depuração
- Para verificar variáveis de ambiente, você adiciona uma etapa
run: env, faz push de novo e entra em um ciclo de feedback de 20 minutos, repetindo esse processo dezenas de vezes por causa de uma mudança de uma linha - Com ciclos de feedback de 20 minutos se repetindo, o dia inteiro é consumido esperando o CI
- Para verificar variáveis de ambiente, você adiciona uma etapa
- As limitações estruturais do YAML
- O YAML do GitHub Actions é uma forma especial que combina sua própria linguagem de expressões, modelo de objetos de contexto e regras de interpolação de strings
- Se você errar uma única aspas em uma expressão
${{ }}, só vai descobrir que a string se perdeu depois de esperar 4 minutos até o runner subir - A sintaxe das expressões existe em um espaço liminar: complexa demais para ser apenas configuração, limitada demais para ser usada como linguagem de programação de verdade
- A estrutura faz com que a sintaxe seja aprendida pela experiência de falha, e não pela documentação
- Os riscos de segurança do Marketplace
- Ao carregar uma action externa com a sintaxe
uses:, você concede a terceiros não verificados acesso ao repositório, aos segredos e ao ambiente de build - É possível fixar por SHA, mas quase ninguém faz isso e, mesmo fixando, o modelo continua sendo executar código opaco que você não leu com acesso ao
GITHUB_TOKEN - O Marketplace mistura actions mantidas pela comunidade com níveis variados de qualidade, em sua maioria compostas por scripts shell e Dockerfiles
- O gerenciamento de dependências é opaco, e existe a possibilidade de executar código inseguro
- Ao carregar uma action externa com a sintaxe
- As restrições do ambiente de computação
- Os runners padrão do GitHub Actions são runners compartilhados da Microsoft: lentos, com recursos limitados e sem possibilidade de customização significativa
- O custo de runners maiores já é suficiente para fazer o time financeiro mandar um "precisamos conversar" em forma de reunião, e mesmo assim o ambiente continua fora do seu controle
- Existem pelo menos seis startups — como Namespace, Blacksmith, Actuated, Runs-on e BuildJet — especializadas apenas em resolver a lentidão dos runners do GitHub Actions, o que por si só prova a deficiência do ambiente de computação padrão
- Configurar um self-hosted runner resolve o problema de computação, mas todos os outros — expressões YAML, modelo de permissões, Marketplace, visualizador de logs — continuam iguais
Problemas menores, mas cumulativos
actions/cache: as chaves de cache são confusas, cache miss acontece em silêncio e a remoção de cache (eviction) é opaca, fazendo você gastar mais tempo depurando cache do que economiza com ele- Workflows reutilizáveis: não podem ser aninhados além de certa profundidade, não acessam de forma limpa o contexto do workflow chamador e não permitem testes isolados
- Modelo de permissões do
GITHUB_TOKEN:permissions: write-allé amplo demais, e permissões granulares viram um labirinto por causa da interação entre configurações em nível de repositório, workflow e job - Controle de concorrência (
concurrency): cancelar execuções em andamento no mesmo branch dá para fazer com uma linha, mas controles mais finos do que isso não são suportados - Impossibilidade de usar secrets em condições
if: uma execução condicional comoif: secrets.DEPLOY_KEY != ''não funciona e, embora isso seja razoável do ponto de vista de segurança, ainda exige soluções alternativas ao escrever workflows que funcionem tanto em forks quanto no repositório principal
A armadilha do "vamos só usar script Bash"
- Engenheiros cansados do YAML de CI sentem a tentação de substituir tudo por scripts bash em
run:, mas com o tempo vão sendo adicionados condicionais, funções, parsing de argumentos e paralelismo - Três meses depois, surgem 800 linhas de bash reimplementando paralelismo de jobs com
waite arquivos PID, além de lógica própria de retry e parsing de saída - No fim, você não escapou do sistema de CI: apenas construiu manualmente em bash um sistema de CI pior, sem framework de testes e que ninguém consegue acompanhar
- Bash é adequado como cola (glue), mas usá-lo como sistema de build ou harness de testes apenas move a complexidade de um lugar com guardrails para outro sem nenhum
A abordagem alternativa do Buildkite
-
Visualizador de logs confiável
- O visualizador de logs do Buildkite mostra logs corretamente sem travar o navegador, renderizando cores ANSI e a formatação dos frameworks de teste como deveriam aparecer
- Com o recurso de Annotation, uma etapa de build pode exibir diretamente na página do build, em Markdown, resumos de falhas de teste, relatórios de cobertura e links de deploy
- Como os agentes rodam na sua própria infraestrutura, é possível acessar a máquina de build por SSH e depurar diretamente
-
Estrutura YAML simples
- O YAML do Buildkite é uma estrutura de dados pura que descreve o pipeline, declarando apenas etapas, comandos e plugins
- Quando lógica de verdade é necessária, ela é escrita em uma linguagem de programação real que pode ser executada localmente
- Isso mantém clara a fronteira de "orquestração na configuração, lógica no código", exatamente a fronteira que o GitHub Actions embaralha
-
Controle total sobre o ambiente de computação
- O agente do Buildkite é um binário único, capaz de rodar na sua própria nuvem, on-premises ou hardware customizado
- Você pode controlar completamente tipo de instância, cache, armazenamento local e rede, desde grandes instâncias EC2 com discos NVMe e 20 GB de cache de camadas Docker até um Raspberry Pi
- Não existe uma indústria de terceiros do tipo "Buildkite, só que mais rápido"; basta rodar uma máquina maior
- Para o mantenedor individual de uma pequena biblioteca open source, o tier gratuito do GitHub Actions para repositórios públicos continua valendo a pena
- O foco principal deste texto são equipes que operam sistemas de produção, onde o tempo de CI é medido como perda semanal de horas de engenharia e builds de 45 minutos geram custo tanto em computação quanto em mão de obra
- Nesses ambientes, o overhead de operar agentes do Buildkite se paga rapidamente
-
Suporte a pipelines dinâmicos
- No Buildkite, as etapas do pipeline são dados, e scripts podem gerar (emit) e enviar novas etapas dinamicamente em tempo de execução
- Em monorepos, isso permite gerar exatamente as etapas de build e teste necessárias com base nos arquivos alterados, sem matrizes hardcoded nem espaguete de
if: contains(...) matrix, condiçõesife workflows reutilizáveis do GitHub Actions tentam aproximar isso, mas acabam fazendo você montar uma máquina de Rube Goldberg em uma linguagem declarativa com pouca expressividade
-
Simplicidade da estrutura de plugins
- Estruturalmente, ela é parecida com o Marketplace do GitHub Actions por também buscar código em repositórios de terceiros
- A diferença é que os plugins do Buildkite costumam ser thin shell hooks, e não imagens Docker, o que reduz a superfície e permite ler tudo em poucos minutos
- Como tudo roda na sua própria infraestrutura, o usuário pode controlar o blast radius
-
Detalhes voltados para a experiência do usuário
- O Buildkite permite mostrar emojis personalizados (
:parrot:,:docker:etc.) ao lado das etapas do pipeline; pode parecer algo pequeno, mas mostra um cuidado minucioso com a experiência de uso do produto - O GitHub Actions parece um produto desenhado por comitê que nunca perguntou: "isso é prazeroso de usar?"
- O Buildkite permite mostrar emojis personalizados (
Conclusão: critérios para escolher um sistema de CI
- O GitHub Actions dominou o mercado pela vantagem de ser o default, com repositórios públicos gratuitos, integração na plataforma que todo mundo já usa e um nível "bom o suficiente"
- É o Internet Explorer do CI: continua sendo usado porque o custo de migração é real e o tempo é finito
- O Buildkite é superior em usabilidade contínua e experiência do desenvolvedor
- Para projetos open source simples, o GitHub Actions basta, mas em ambientes grandes de produção o Buildkite é mais adequado
- Na história dos sistemas de CI, quem ganha participação de mercado não é o melhor sistema, e sim o sistema mais fácil de começar a usar
- O GitHub Actions é o CI mais fácil de começar, e o Buildkite é o CI melhor para continuar usando; no longo prazo, isso importa mais
- Se a estrutura da ferramenta de CI está consumindo o tempo dos desenvolvedores, o problema não são os desenvolvedores, e sim a própria ferramenta
3 comentários
Parece que o problema é a própria complexidade crescente do CI.
Parece que o mesmo texto foi publicado duas vezes. Ainda assim, hoje em dia isso até parece uma boa combinação para a IA montar..
Comentários no Hacker News
Já usei vários sistemas de CI. Usei bastante CircleCI e GitHub Actions, mas cheguei a uma conclusão diferente da do autor
Antigamente, Jenkins era voltado para Java e Travis para Rails, mas esse tipo de CI especializada acabou sendo um beco sem saída. Hoje, CI evoluiu para ser simplesmente um orquestrador de workflows
O motivo de eu ter migrado do CircleCI 2 para o GitHub Actions também foi porque o CircleCI não conseguiu fazer bem essa transição. O GHA tinha expressividade suficiente
Coisas como navegador de logs ou sintaxe YAML são problemas menores. O importante é ter controle sobre os recursos de computação e pipelines dinâmicos; o primeiro é possível em qualquer CI, e o segundo é um ponto forte do Buildkite
Minha conclusão é que o Actions é, na prática, bem decente, e se eu começasse uma empresa nova usaria Buildkite; para open source, usaria Actions
Se você não entende o grafo de build, precisa manter estado de build incremental, e isso provoca bugs intermitentes. Por isso são necessários sistemas como UnrealEngine Horde ou UBA, que entendem profundamente a estrutura de build
Com uma CI generalista, um build pode levar mais de um dia
Eu costumo usar só as partes boas do GHA. Por exemplo, o GitHub é excelente como despachante de eventos, mas ruim como orquestrador de workflows, então delego essa parte a outro sistema
Se os logs que você vê dezenas de vezes por dia são ruins, a produtividade cai. Ler logs brutos ignorando códigos de escape é realmente doloroso
Eu mantenho a simplicidade. Coloco toda a orquestração no script deploy.sh e executo localmente no Mac ou no AWS CodeBuild
O YAML é só uma linha:
bash deploy.sh. Se houver um contêiner Docker, ele funciona igual em qualquer lugar, como Azure ou GitHub ActionsA estratégia central em qualquer ambiente de CI é ter um sistema de build idêntico ao local
Eu sempre começo com um Makefile. Docker, builds de CI, linting, tudo é acionado pelo Makefile. Quando o projeto cresce, às vezes migro para outras ferramentas, mas a base é uma única ferramenta de disparo
Eu uso muito Fastlane em mobile, e ele reduz boilerplate e fornece estrutura. No fim, continua sendo Ruby, então dá para escapar quando necessário
(link para a documentação do GNU Make)
No fim, isso equivale a dizer “é só usar um script Bash”, mas com complexidade desnecessária adicionada
Na empresa atual, como já não é possível rodar o pipeline inteiro localmente, surgiu uma infraestrutura gigantesca de CI em que se roda build 10 vezes para testar um único MR
Achei que este texto parecia publicidade de Nix/Buildkite
A CI deveria só executar scripts ou alvos de build. A CI deve fornecer ambiente e configuração, e a lógica deve ficar no código
Isso traz independência da CI, facilitando a migração entre sistemas
O GitLab CI lida relativamente bem com essa complexidade. Seus templates e recursos de composição de jobs são excelentes, mas é difícil depurar, e a lógica condicional às vezes falha de forma inesperada
.shtiveram uma migração muito fácilJá as equipes que fragmentaram tudo na UI sofreram bastante
O problema não é CI/CD em si, e sim a cultura de programar em arquivos de configuração
O ciclo de
git commit -m "try fix"e depois esperar 10 minutos é comum demais. Ainda faltam ambientes de CI reproduzíveis localmenteSe o isolamento de ambientes for definido por política, qualquer ferramenta funciona bem. No fim, o essencial é a harmonia entre ferramenta e metodologia
actajudam bastante a reproduzir CI localmenteO título dizendo que “está matando equipes de engenharia” é exagerado. GitHub Actions é bom o suficiente
Eu prefiro a Bitbucket ou GitLab
A queda de confiabilidade do GitHub tem sido séria recentemente
O
actions/checkoutfalha sem motivo, jobs de release rodam duas vezes, e às vezes fica 40 minutos só esperandoUso há anos, mas a estabilidade básica está piorando. Pena que deixei passar o Buildkite
GitHub Actions é uma das piores ferramentas de CI que já usei (no nível do Jenkins)
Em contrapartida, o Buildkite é o melhor. Graças aos pipelines dinâmicos, ele pode criar etapas de retry automaticamente quando um teste falha, ou ajustar testes paralelos com base nas mudanças no código
Também é uma grande vantagem poder usar uma configuração de máquina diferente para cada job de CI. Recomendo fortemente
As pessoas que defendem uma “CI simples baseada em scripts” parecem não ter experiência com projetos reais em grande escala