Publicado em
- 8 minutos de leitura
Nx 24: task-based affected, Ultracache e task packing
O Nx 24 foi publicado em 21 de outubro de 2026 e mudou a forma como o Nx decide o que precisa rodar em cada PR. A versão transforma o task-based affected no comportamento padrão e remove os executores internos que já tinham um plugin equivalente.
No dia seguinte, o time do Nx anunciou duas funcionalidades do Nx Cloud que fecham o ciclo: Ultracache e dynamic task packing. As duas são vendidas como a maior mudança no CI da plataforma até hoje, com números que interessam a qualquer monorepo grande.
Neste artigo, vamos ver o que mudou no Nx 24, como o affected por tarefa funciona na prática e como avaliar as duas novidades do Nx Cloud.
O que muda no Nx 24
O nx affected sempre trabalhou no nível de projeto.
Você altera um arquivo, o Nx descobre qual projeto é dono dele e marca como afetados todos os projetos que dependem desse projeto.
Depois, roda o target pedido em todos eles.
Essa abordagem é conservadora e segura, mas roda mais do que o necessário.
No Nx 24, a seleção passa a ser por tarefa: entra no grafo apenas o target que lê um dos arquivos alterados.
Se você mexer em um arquivo spec de uma lib compartilhada, só o target test daquela lib é afetado.
Os builds dos projetos dependentes ficam de fora, porque não leem aquele arquivo.
Para atualizar, o caminho continua o mesmo:
npx nx migrate latest
npx nx migrate --run-migrations
Revise o migrations.json gerado, rode os testes e valide o pipeline em uma branch antes de levar a mudança para main.
A versão estável disponível no npm hoje é a 24.0.1, publicada em 23 de outubro.
Como o task-based affected decide o que rodar
A regra é a mesma que sustenta o cache: os inputs de uma tarefa definem seu comportamento. Quando um input muda, o comportamento muda e a tarefa é afetada. Isso vale para tarefas com cache e sem cache.
O Nx parte dos arquivos alterados que o Git informa. Toda tarefa que declara um desses arquivos como input é tocada diretamente. Uma tarefa tocada produz outputs diferentes, então cada tarefa que lê esses outputs também é afetada, e o processo segue pelo grafo. Tarefas que não mudam de comportamento são removidas da execução.
O ponto prático é que dependsOn sozinho não propaga a mudança.
Uma aresta que só ordena duas tarefas não carrega o arquivo alterado de uma para a outra.
A propagação acontece pelos inputs, como ^production, ou pelos outputs lidos via dependentTasksOutputFiles.
Na configuração abaixo, build lê os arquivos de produção do próprio projeto e das dependências, enquanto test lê todos os arquivos do projeto e a produção das dependências:
// nx.json
{
"namedInputs": {
"production": ["{projectRoot}/**/*", "!{projectRoot}/**/*.spec.ts"]
},
"targetDefaults": {
"build": { "inputs": ["production", "^production"] },
"test": { "inputs": ["{projectRoot}/**/*", "^production"] }
}
}
Com isso, um spec alterado em libs/ui seleciona apenas ui:test.
Antes, o mesmo cenário podia selecionar dezenas de tarefas de teste em projetos que nem importavam o arquivo.
Ver por que uma tarefa entrou na lista
O flag --explain mostra o motivo da seleção sem executar nada:
nx affected -t build --explain
A saída lista quantas tarefas estão afetadas e quais arquivos tocaram quais targets.
Com --verbose, o Nx detalha cada motivo.
Isso ajuda a encontrar inputs mal declarados, que fazem o pipeline rodar demais ou de menos.
Voltar ao comportamento antigo
O task-based affected é o padrão no Nx 24, mas dá para desligar.
No nx.json, use o bloco affected:
// nx.json
{
"affected": {
"legacy": true
}
}
Para um único comando, use a variável de ambiente NX_LEGACY_AFFECTED=true.
O flag vale para nx affected -t e para nx show projects --affected -t.
O modo antigo ainda é útil em workspaces com inputs incompletos, porque ele erra para o lado de rodar demais. O modo novo é mais preciso e depende de inputs e outputs bem declarados.
Ultracache: um cache que aprende com execuções reais
Cache remoto tradicional depende do que você declara em configuração. Se os inputs listados incluem arquivos que a tarefa não lê, qualquer mudança neles invalida o cache sem necessidade. Se falta um arquivo que a tarefa lê, o cache pode devolver um resultado desatualizado.
O Ultracache inverte a lógica. Ele observa quais arquivos cada tarefa lê e escreve durante a execução, guarda esse mapa e o consolida entre execuções. Na primeira execução em um worker, a tarefa é hasheada pelos inputs declarados e o registro começa. Nas execuções seguintes em CI, o hash usa o registro, que cresce quando a tarefa lê um arquivo novo.
O resultado é mais acerto de cache com invalidação correta, sem manter listas de arquivos à mão. Segundo a documentação, a mudança pode reduzir o compute do CI em até 3 vezes em alguns casos.
Como ativar o Ultracache
Workspaces novos no Nx Cloud já começam com o recurso ligado. Em workspaces existentes, abra as configurações do workspace no Nx Cloud e ligue a opção Ultracache.
O recurso exige Nx 24.0.0 ou superior, porque é essa versão que o usa em CI por padrão.
Para pular o Ultracache em uma execução pontual:
NX_CLOUD_USE_ULTRACACHE=false npx nx affected -t build
Para um target específico, desligue no project.json:
// project.json
{
"targets": {
"e2e": {
"ultracache": { "mode": "off" }
}
}
}
Também existem ultracache.ignoreReads e ultracache.ignoreWrites para deixar diretórios temporários de ferramentas fora do registro.
O recurso está incluído em todos os planos do Nx Cloud, inclusive o Hobby, e tarefas rastreadas não consomem créditos.
Vale saber o que não é rastreado: cache hits, tarefas contínuas, targets com cache: false e tarefas executadas por batch executor.
Só execuções em workers geram registro, mas todo run de CI usa o registro no cálculo do hash.
Dynamic task packing: menos máquinas, mais utilização
A maioria dos pipelines distribui trabalho de forma estática. Testes end-to-end vão para uma máquina, testes unitários para outra, builds para uma terceira. Esse desenho não se adapta quando um PR roda 10 tarefas e o próximo roda 200.
O dynamic task packing olha o run inteiro. Ele coleta todas as tarefas da execução, estima consumo de CPU e memória a partir do histórico, e encaixa cada tarefa em um worker conforme a capacidade disponível naquele momento. Tarefas que destravam outras são priorizadas, então um build que precede um e2e entra cedo.
Na prática, o recurso dispensa ajuste de --parallel e mapas de projeto para máquina.
Um lint leve pode dividir o worker com um e2e pesado em memória, desde que os dois caibam.
Se um worker falha na preparação, os outros assumem as tarefas.
Como ligar o dynamic task packing
Com o workspace conectado ao Nx Cloud, o setup mínimo é um arquivo de configuração de CI:
# .nx/ci-config.yaml
dte:
distribute-on: 3 linux-large-js
Depois, ative a opção Dynamic task packing nas configurações do workspace no Nx Cloud.
Variáveis definidas no job, como NX_CLOUD_AUTO_TASK_PACKING, têm precedência sobre a configuração do workspace.
Em workspaces que já usam workers, vale revisar restrições antigas.
Valores de --parallel que existiam para controlar CPU e memória deixam de ser necessários, porque o packing mede o consumo real.
Restrições que continuam válidas são as de recurso compartilhado na mesma máquina, como uma porta fixa ou um banco local.
Os números do benchmark
O Nx publicou dois benchmarks com o conjunto completo: task-based affected, dynamic task packing e Ultracache.
- Monorepo com 511 projetos: 9,2x mais rápido com 52% menos compute.
- Aplicação React única com cerca de 300 mil linhas: 11,7x mais rápido com 78% menos compute.
O segundo caso é o mais interessante. Em um monólito, o cache por projeto ajuda pouco, porque qualquer mudança invalida o projeto inteiro. Como o Ultracache trabalha com arquivos lidos de verdade, ele consegue separar o que realmente mudou mesmo dentro de uma aplicação grande.
Os dois repositórios de benchmark estão abertos: cache-dte-bench-2 e monolith-bench-2.
O que mais quebra ao subir para o Nx 24
A mudança com maior impacto em workspaces existentes é a remoção dos executores internos que já tinham um plugin de inferência.
São 37 executores em 15 pacotes, incluindo @nx/jest:jest, @nx/webpack:webpack, @nx/vite:build, @nx/playwright:playwright e @nx/storybook:storybook.
O gerador que faz a conversão já existe desde o Nx 19:
npx nx g infer-targets
Ele registra o plugin no nx.json, compara o target que seria inferido com o que está no project.json e mantém apenas a diferença.
No fim, nx show project <projeto> --web mostra cada target, a origem e as opções aplicadas.
Outros pontos de atenção na atualização:
- Os geradores SCAM do
@nx/angularforam removidos; usecomponent,directiveepipe. - A opção
setParserOptionsProjectsaiu dos geradores; o substituto éenableTypedLinting. @nx/dotnetnão infere mais um targetrestore; é preciso rodardotnet restoreou ligarrestore: truenas opções do plugin.- Plugins de framework deixaram de inferir
typecheck; a responsabilidade fica com@nx/js/typescript. - Dev servers agora são aguardados por padrão, com timeout de 10 minutos.
- Module Federation saiu dos plugins do Nx e passou para os plugins oficiais.
Se o seu nx.json tem "useInferencePlugins": false, o infer-targets não faz nada de útil.
Remova o flag antes de rodar o gerador.
Segurança do cache remoto
Ligar cache remoto aumenta o ganho de velocidade, mas exige cuidado com envenenamento de cache. Uma entrada adulterada pode ser restaurada como output real em builds seguintes.
O Nx Cloud trata isso com entradas imutáveis, tokens com escopo e isolamento de escrita em pull requests.
A recomendação é usar token de leitura e escrita só em branches protegidas, como main.
Em PRs, um token somente leitura lê o cache compartilhado e grava em um cache isolado daquele run.
Para o artefato que vai para produção, vale pular o cache na hora de gerar o build. Assim, mesmo que algo entre errado no cache, o deploy não usa esse resultado.
Conclusão
O Nx 24 não é só mais uma minor. Ele muda o padrão do affected para tarefas, remove os executores que viraram dívida técnica e prepara o terreno para o Ultracache funcionar em CI.
A combinação de task-based affected, Ultracache e dynamic task packing ataca as duas fontes de desperdício no CI: rodar tarefas que não mudaram e ocupar máquinas ociosas. Se o seu monorepo já usa Nx Cloud, o caminho é atualizar, ligar as duas opções no workspace e medir o tempo de pipeline antes e depois.
Se você ainda está no Nx 23, comece pelo nx migrate latest e pelo nx g infer-targets.
A conversão é o passo que destrava a atualização com menos surpresas.
Entre na nossa comunidade!
Receba novos posts, novidades do ecossistema Angular e muito mais.
Sobre o autor