Por que testar arquitetura em um mundo polyrepo?
Na Netflix, o ecossistema JVM tem dezenas de milhares de repositórios Java. Sem uma forma centralizada de aplicar regras arquiteturais, a dívida técnica se acumula silenciosamente — especialmente em APIs obsoletas ou internas. O desafio: como impedir que consumidores downstream usem APIs que os autores pretendem remover?
Ferramentas tradicionais de análise estática (PMD, SpotBugs) são limitadas para criação de regras customizadas. Suas linguagens de regra (XPath, XML) são opacas, difíceis de testar e específicas de linguagem. O ArchUnit, construído sobre análise de bytecode ASM, oferece uma API Java fluente que é type-safe e fácil de testar unitariamente. Mas o ArchUnit sozinho é projetado para uso em um único repositório. O Nebula ArchRules preenche essa lacuna, permitindo que organizações definam, publiquem e executem regras ArchUnit em centenas ou milhares de projetos Gradle.
Estudo de caso: Gerenciamento do ciclo de vida de APIs em escala
Após um incidente causado pela remoção de código obsoleto (depreciado por anos, mas ainda usado downstream), o time de Java Platform da Netflix precisava de uma solução sistemática. Eles introduziram um conjunto de anotações de ciclo de vida de API:
@Deprecated(Java padrão)@Public— para uso downstream@Experimental— API instável- Sem anotação = interno
Autores de bibliotecas podiam marcar suas APIs, mas como saberiam quais projetos downstream as usavam incorretamente? A resposta: Nebula ArchRules.
Como o Nebula ArchRules funciona
1. Escrevendo regras compartilháveis
O ArchRules Library Plugin adiciona um source set separado archRules. Você implementa a interface ArchRulesService, retornando um Map<String, ArchRule>. Exemplo:
public class GuavaRules implements ArchRulesService {
static final ArchRule OPTIONAL = ArchRuleDefinition.priority(Priority.MEDIUM)
.noClasses()
.should()
.dependOnClassesThat()
.haveFullyQualifiedName("com.google.common.base.Optional")
.because("Java Optional é preferível ao Guava Optional");
@Override
public Map<String, ArchRule> getRules() {
Map<String, ArchRule> rules = new HashMap<>();
rules.put("guava optional", OPTIONAL);
return rules;
}
}
As regras são empacotadas em um JAR separado com o classifier arch-rules, publicado junto com a biblioteca principal. Existem dois sabores:
- Bibliotecas de regras standalone: contêm apenas regras (ex.: “não use APIs
@Deprecated”). Úteis para APIs OSS ou Java core. - Bibliotecas de regras bundled: incluem código principal e regras específicas do pacote da biblioteca. Aplicadas automaticamente quando projetos downstream dependem da biblioteca.
2. Executando regras entre repositórios
O ArchRules Runner Plugin descobre e avalia regras automaticamente. Para regras standalone, adicione-as à configuração archRules:
dependencies {
archRules("com.netflix.nebula:nebula-archrules-deprecation:1.0.0")
}
Regras bundled são avaliadas automaticamente. O plugin cria um classpath separado para cada source set, combinando o runtime classpath com a variante arch-rules. Uma Gradle work action executa o ArchUnit com isolamento de classpath, e as violações são serializadas em um arquivo binário.
3. Customização e relatórios
Você pode sobrescrever prioridades de regras, desabilitar regras em certos source sets ou definir thresholds de falha:
archRules {
ruleClass("com.netflix.nebula.archrules.deprecation") {
priority("HIGH")
}
}
Dois relatórios nativos: JSON (legível por máquina) e console (legível por humanos com apontamento de linha). Relatórios customizados podem ler os arquivos binários diretamente.

Impacto real: 358 regras, 5.000+ repositórios, 1 milhão de problemas detectados
A Netflix executa atualmente 358 regras em mais de 5.000 repositórios, detectando quase 1 milhão de problemas. Cerca de 1.000 são violações de alta prioridade. Os dados vão para o Portal Interno de Desenvolvedores da Netflix, dando visibilidade aos autores de bibliotecas sobre exatamente quais consumidores downstream usam APIs obsoletas ou experimentais.
Exemplo: Detectando uso de APIs obsoletas
ArchRuleDefinition.priority(Priority.MEDIUM)
.noClasses()
.that(resideOutsideOfPackage(packageName + ".."))
.should()
.dependOnClassesThat(resideInAPackage(packageName + "..")
.and(are(deprecated())))
.orShould()
.accessTargetWhere(targetOwner(resideInAPackage(packageName + ".."))
.and(target(is(deprecated()))
.or(targetOwner(is(deprecated())))))
.allowEmptyShould(true)
.because("APIs obsoletas estão sujeitas a remoção");
Esta regra captura tanto dependência direta em classes obsoletas quanto acesso a métodos ou campos obsoletos — tudo escopo ao pacote de uma biblioteca específica.
Limitações e cuidados
- Apenas Gradle: O plugin depende de Gradle Module Metadata e configurações customizadas. Maven ou outros sistemas de build não são suportados.
- Bytecode ≠ Código fonte: ArchUnit analisa bytecode compilado, o que pode perder código escondido por açúcar sintático (ex.: funções inline do Kotlin que expandem para bytecode sem anotações).
- Performance: Executar 358 regras em milhares de repositórios exige design cuidadoso de pipeline de CI. A Netflix executa em todo build de branch principal, o que adiciona overhead.
- Manutenção de regras: Regras precisam ser mantidas atualizadas com mudanças nas bibliotecas. Uma regra desatualizada que sinaliza uma API irrelevante gera ruído.
Próximos passos
A Netflix está explorando automação de correção combinando os relatórios detalhados de falha do ArchUnit com ferramentas como OpenRewrite (determinístico) e LLMs (não determinístico). O objetivo: corrigir violações automaticamente sempre que possível. Também planejam exibir violações do ArchRule como inspeções na IDE.
Se você está construindo uma organização JVM polyrepo, comece pequeno: escreva algumas regras bundled para suas bibliotecas principais, habilite o runner no CI e itere. As bibliotecas de regras OSS (nullability, boas práticas Gradle, segurança) são um ótimo ponto de partida.
Para mais sobre uso responsável de IA em código, confira nosso guia sobre como alavancar agentes de IA de forma responsável. E se você está avaliando ferramentas de IA para seu workflow, leia Beyond the Hype: Um Guia para Desenvolvedores Responsáveis sobre Ferramentas de IA.

Principais diferenças
| Aspecto | ArchUnit + Nebula ArchRules | Análise estática tradicional (PMD/SpotBugs) |
|---|---|---|
| Criação de regras | API Java fluente, type-safe, testável unitariamente | Strings XPath/XML, sem suporte de IDE |
| Suporte a linguagens | Nível de bytecode (qualquer linguagem JVM) | Baseado em AST (específico de linguagem) |
| Compartilhamento | Gradle Module Metadata + variante arch-rules | Ecossistema de plugins, sem compartilhamento padronizado |
| Escalabilidade | 5.000+ repositórios, 358 regras | Tipicamente repositório único |
| Customização | Sobrescrita de prioridade, exclusão de source sets | Limitada à configuração do plugin |
Conclusão
O Nebula ArchRules transforma o ArchUnit de uma biblioteca de teste de repositório único em uma plataforma de enforcement de arquitetura em toda a frota. Combinando facilidade de criação de regras com descoberta e relatórios automatizados, a Netflix reduziu o risco de mudanças que quebram e tornou a dívida técnica visível em escala. A abordagem é pragmática: comece com algumas regras, itere com base em incidentes reais e deixe os dados guiarem suas prioridades.
Este artigo é baseado no post original do Netflix Tech Blog por John Burns e Emily Yuan. Para detalhes técnicos completos, consulte o artigo original.
