Resumo rápido
- A ideia por trás da computação incremental vem de uma linha de pesquisa acadêmica chamada self-adjusting computation, formalizada no projeto Adapton (Hammer, Phang Khoo, Hicks e Foster, PLDI 2014), que descreve um grafo de dependências acionado por demanda.
- Hoje esse modelo roda em produção em pelo menos três lugares concretos: o Salsa usado pelo rust-analyzer, a biblioteca Incremental da Jane Street em OCaml e o motor de cache por função do Turbopack, sucessor do Webpack no Next.js.
- React não implementa esse modelo de grafo persistente: a reconciliation dele compara árvores inteiras (diffing) a cada render, um problema parecido resolvido de um jeito diferente.
Imagine uma planilha complexa com centenas de células ligadas por fórmulas. Quando você altera um único valor, apenas as células que dependem daquele dado são recalculadas, não a planilha inteira. Esse comportamento é a essência da computação incremental: um jeito de estruturar programas para que, quando uma entrada muda, só o subconjunto de cálculos afetado seja refeito.
Não é um conceito de marketing nem exclusivo de uma linguagem. Você encontra essa ideia rodando por trás do editor que analisa seu código Rust em tempo real, dentro de um bundler de JavaScript e em sistemas financeiros escritos em OCaml.
O problema da recomputação desnecessária
Em muitas aplicações, você tem dados de entrada que mudam ao longo do tempo e computações derivadas desses dados. A abordagem ingênua é recalcular tudo sempre que algo muda. Isso funciona bem para cálculos rápidos, mas vira gargalo quando:
- Os cálculos são computacionalmente custosos.
- Os dados de entrada mudam com alta frequência.
- Apenas uma fração pequena dos dados muda em cada atualização.
- Há várias camadas de transformações e agregações interdependentes.
Pense em um dashboard financeiro que monitora milhares de ativos, calcula indicadores técnicos e agregações por setor. Se o preço de uma única ação muda, não faz sentido recalcular estatísticas de setores completamente não relacionados.
De onde vem a ideia: o grafo de dependências do Adapton
A base teórica mais citada para computação incremental moderna é o trabalho de self-adjusting computation, e o marco mais conhecido dessa linha é o Adapton, apresentado por Matthew Hammer, Yit Phang Khoo, Michael Hicks e Jeffrey Foster na PLDI 2014. A proposta central é simples de enunciar e difícil de implementar bem: em vez de propagar mudanças imediatamente por todo o grafo (modelo push), você deixa o sistema recalcular sob demanda, só quando alguém realmente pede o valor atualizado (modelo pull, ou “demand-driven”).
Essa separação entre “o dado mudou” e “alguém precisa do resultado agora” é o que permite lotes de mudanças serem processados de forma eficiente, sem recomputação em cascata a cada pequena alteração.
Salsa: o motor que mantém o rust-analyzer responsivo
Um dos usos mais visíveis dessa ideia em produção é o Salsa, um framework Rust genérico para computação incrementalizada sob demanda. Ele foi criado para o rust-analyzer, o servidor de linguagem que dá autocompletar, navegação e diagnósticos em tempo real para quem programa em Rust.
O próprio projeto Salsa cita como inspiração o Adapton, o sistema de templates Glimmer e o query system do próprio compilador rustc. A ideia de query system aqui é direta: você define seu programa como um conjunto de funções puras que mapeiam uma chave para um valor, o Salsa memoiza os resultados e, quando uma entrada muda, recalcula só as queries cujo resultado pode ter mudado. O time do rust-analyzer documentou essa evolução no artigo Durable Incrementality, que detalha como o sistema evoluiu para persistir cache entre sessões do editor.
A biblioteca Incremental da Jane Street
Do lado do OCaml, a Incremental é uma biblioteca open source da Jane Street, descrita no post Introducing Incremental do blog da empresa, também inspirada em self-adjusting computation. Ela trabalha com alguns conceitos centrais que vale a pena conhecer:
Incremental values são os valores que fazem parte do sistema: em vez de trabalhar diretamente com valores normais de OCaml, você trabalha com valores do tipo 'a Incremental.t, que carregam suas próprias dependências rastreadas.
Variables são os pontos de entrada, valores mutáveis que você atualiza externamente. Quando uma variável muda, todas as computações que dependem dela são marcadas para recálculo.
Observers permitem extrair o valor atual de uma computação incremental. São os pontos de saída, onde você efetivamente lê os resultados.
Stabilization é o processo de executar as recomputações pendentes depois de mudanças nas entradas. A biblioteca não recalcula na hora em que uma variável muda; você chama explicitamente uma função de estabilização que processa todas as atualizações pendentes em lote.
A própria Jane Street usa a Incremental como base do Bonsai, seu framework de UI: quando o estado da aplicação muda, só os componentes afetados são reprocessados, não a árvore inteira.
Turbopack: incremental aplicado a build de front-end
Do lado de ferramentas de build, o Turbopack, sucessor do Webpack usado pelo Next.js, também adota esse modelo. Segundo a própria documentação, o Turbopack cacheia resultados no nível de função: uma vez que um pedaço de trabalho é feito, ele não é repetido, e o sistema rastreia automaticamente como funções internas se chamam e de quais valores dependem para saber o que recalcular quando algo muda. A equipe do Turbopack cita explicitamente Salsa, Adapton e o query system do rustc como referências de design no post Inside Turbopack: Building Faster by Building Less.
Isso é um contraste interessante com o scriptc, o compilador experimental da Vercel Labs que compila TypeScript direto para binário nativo. Enquanto o Turbopack ataca o custo de rebuild recalculando só o necessário, o scriptc ataca um problema adjacente, o de eliminar runtime embutido, do outro lado do processo de build. São estratégias diferentes para reduzir o custo total de compilar e rodar código.
E o React? Reconciliation não é o mesmo mecanismo
É tentador colocar o React na mesma prateleira, já que ele também evita “recalcular tudo”. Mas vale separar os conceitos: a reconciliation do React funciona comparando a árvore de elementos gerada em cada render com a árvore anterior (diffing), e não mantendo um grafo de dependências persistente com memoização fina como Salsa ou Incremental fazem. O React ainda executa a função de renderização do componente e depois compara o resultado; sistemas como Salsa e Incremental evitam até essa reexecução quando possível, porque sabem de antemão quais nós do grafo foram invalidados.
São dois jeitos válidos de atacar o mesmo problema de fundo (evitar trabalho redundante), mas com garantias e trade-offs diferentes.
Quando vale a pena considerar computação incremental
Alguns cenários onde esse modelo costuma compensar o esforço de adoção:
Interfaces reativas complexas, como o Bonsai da Jane Street, onde só os componentes afetados por uma mudança de estado precisam ser reprocessados.
Processamento de streams financeiros, em sistemas de trading onde milhares de cotações chegam por segundo e você precisa atualizar indicadores sem recalcular tudo a cada tick.
Compilação e build systems, como Salsa e Turbopack demonstram, recompilando só os artefatos afetados por uma mudança e seus dependentes.
Análise de dados interativa, onde parâmetros e filtros mudam com frequência e os resultados (gráficos, estatísticas) precisam atualizar rápido sem reprocessar todo o dataset.
Diferenças para sistemas reativos tradicionais e para paralelismo puro
Bibliotecas reativas como RxJS costumam propagar mudanças imediatamente (push), enquanto sistemas como Salsa e Incremental usam um modelo mais próximo de pull, com estabilização explícita, o que permite processar várias mudanças em lote de forma eficiente.
Vale notar também que computação incremental resolve um problema diferente do de paralelismo puro. Se o gargalo do seu sistema é volume de trabalho que pode ser distribuído entre núcleos, e não trabalho redundante sendo refeito, a resposta certa provavelmente não é um grafo incremental, é paralelismo de verdade. É a mesma distinção que aparece na armadilha Tokio/Rayon em Rust: escolher a ferramenta errada para o tipo de problema (concorrência quando o caso pede paralelismo, ou vice-versa) cria complexidade sem entregar o ganho de performance esperado. Otimizar o algoritmo em si, como o codec misa77 faz para descompressão, é outra estratégia de performance ainda distinta: reduzir o custo de cada operação, em vez de reduzir quantas vezes ela roda.
Perguntas frequentes
O que é computação incremental, resumindo em uma frase? É a técnica de estruturar um programa como um grafo de dependências para que, quando uma entrada muda, só os cálculos que realmente dependem dela sejam refeitos.
Computação incremental é o mesmo que programação reativa? Não exatamente. Programação reativa (como RxJS) tende a propagar mudanças imediatamente. Sistemas incrementais como Salsa e Incremental costumam separar “a entrada mudou” de “recalcule agora”, processando atualizações sob demanda ou em lote.
Preciso programar em OCaml ou Rust para usar essas ideias? Não. O paradigma é aplicável em qualquer linguagem. A Incremental é OCaml e o Salsa é Rust, mas o princípio de dependência rastreada e recomputação seletiva pode inspirar arquitetura em qualquer stack, inclusive em como você desenha cache e invalidação no seu próprio sistema.
React usa computação incremental? Ele resolve um problema parecido (evitar atualizar tudo na tela) mas com outro mecanismo: diffing de árvores a cada render, não um grafo de dependências persistente e memoizado como Salsa ou Incremental.
Considerações finais
Computação incremental não é uma biblioteca específica, é um padrão que aparece de formas diferentes dependendo de onde você olha: como pesquisa acadêmica em Adapton, como motor de linguagem em Salsa, como biblioteca de produção em Incremental, e como estratégia de cache em Turbopack. Se você trabalha com pipelines de dados que mudam com frequência, dashboards em tempo real ou ferramentas de build, entender esse modelo de grafo de dependências e recomputação sob demanda pode indicar onde vale a pena investir tempo de engenharia, mesmo sem adotar nenhuma dessas bibliotecas diretamente.
Recomendação relacionada
O Programador Pragmático: De Aprendiz a Mestre
Um clássico atemporal sobre boas práticas de desenvolvimento de software, útil pra qualquer stack ou linguagem.
Ver na Amazon →Como Associado Amazon, Visão Binária pode ganhar uma comissão sobre compras qualificadas feitas através deste link, sem custo adicional pra você.