Publicado em

- 8 minutos de leitura

Nx 24: task-based affected, Ultracache e task packing

img of 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/angular foram removidos; use component, directive e pipe.
  • A opção setParserOptionsProject saiu dos geradores; o substituto é enableTypedLinting.
  • @nx/dotnet não infere mais um target restore; é preciso rodar dotnet restore ou ligar restore: true nas 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.

Imagem com o texto: Curso Angular Moderno e um homem de pele parda usando óculos com a logo do Angular atrás. Logo abaixo existe um botão com o texto "Eu quero!"

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

Author's photo
Henrique Custódia Arquiteto Frontend, entusiasta Angular, cat lover, criador de conteúdo e fundador da Code Dimension!