Android

1. A Micro-Economia dos Grafos de Construção do Gradle

A escala de desenvolvedores em projetos Android revela que o sistema de build Gradle pode se tornar um grande obstáculo ou acelerador. O problema não está no número de módulos, mas na exposição do bytecode ABI. Entender a diferença entre Compile Classpath e Runtime Classpath é crucial para otimizar o tempo de compilação incremental, evitando que alterações internas afetem toda a árvore de recursos.

Compartilhar
Medium

1. A Micro-Economia dos Grafos de Construção do Gradle

Com cinco desenvolvedores, seu sistema de construção é invisível. Com mais de 50 desenvolvedores distribuídos em 10 squads diferentes, o grafo de construção do Gradle pode ser um acelerador ou a maior parte da sua folha de pagamento.

A maioria das equipes Android reage à dor de escalabilidade criando mais módulos. Elas dividem um monolito em 30 módulos de recursos, conectam-nos via dependências api e se surpreendem quando os tempos de build incremental permanecem acima de 3 minutos.

O problema não é o número de módulos; é a exposição da ABI (Application Binary Interface) em bytecode.

Classpath de Compilação vs. Classpath de Tempo de Execução

Para entender a economia da construção, devemos diferenciar entre dois classpaths gerenciados pelo Gradle:

  1. Classpath de Compilação (compileClasspath): O conjunto de classes visíveis ao compilador quando processa o código-fonte no módulo M. Alterações nas dependências desse classpath acionam a recompilação do M.
  2. Classpath de Tempo de Execução (runtimeClasspath): O conjunto completo de classes embaladas no final DEX/APK. Alterações em dependências apenas para tempo de execução não exigem recompilação do módulo M durante as fases intermediárias de build.
                  +-------------------+
| :app |
+-------------------+
/ \
(impl) / \ (impl)
v v
+-----------------------+ +----------------------+
| :feature:checkout-impl| | :feature:profile-impl|
+-----------------------+ +----------------------+
| | (api) (impl)
(impl) | | (api)
v | (api)
+-----------------------+ | (api)
| :feature:profile-api | <=============+ (runtime)
+-----------------------+
| (api) (api)
(api) | | (api)
v (api)
+-----------------------+
| :core:model |
+-----------------------+ (api)

[ Legenda ] | ---> Dependência de Tempo de Compilação (compileClasspath) | ===> Montagem do Assembly em Tempo de Execução via :app (runtimeClasspath)
Press enter or click to view image in full size

Observe a fronteira arquitetônica-chave: :feature:checkout-impl compila somente contra :feature:profile-api. Ele tem zero visibilidade de tempo de compilação em :feature:profile-impl. O módulo raiz :app costura a implementação no gráfico de runtime.

Modelo Matemático de Invalidação do Gráfico de Construção

Quando um desenvolvedor modifica o código-fonte no módulo M, o Gradle avalia o conjunto de módulos downstream D(M) que dependem transitivamente do compile classpath exposto de M.

O tempo total de compilação incremental é modelado como:

Tempo Total de Compilação = T(M) + Σ T(d)   [para todos d ∈ D(M)]

Onde:

  • T(M): custo de compilação do próprio módulo modificado M.
  • D(M): conjunto de módulos downstream invalidados por mudanças no ABI em M.
  • T(d): custo de compilação para cada consumidor downstream invalidado d.

Nota: Este modelo de primeira ordem isola a invalidação do classpath. O desempenho real é ainda influenciado pela saturação da memória do Gradle Daemon, paralelismo de threads de trabalho, I/O de disco e taxas de acerto em caches de build remotos.

1. Gráfico Monolítico api

Em um gráfico multi-módulo conectado via api, modificar um detalhe de implementação interna em :feature:profile altera sua assinatura pública ABI, expandindo o conjunto de invalidações para toda a árvore da feature:

D(M_monolith) = { :feature:checkout, :feature:search, :feature:cart, ... }

Tempo Total de Compilação = T(profile) + T(checkout) + T(search) + T(cart) + ...

2. Gráfico de Fronteira Decoupled API/Impl

Ao encapsular a lógica privada dentro de :feature:profile-impl e expor apenas interfaces estáveis em :feature:profile-api, uma mudança interna em :feature:profile-impl reduz o conjunto de invalidações downstream para zero módulos irmãos:

D(M_impl) = Ø  (Conjunto Vazio)

Tempo Total de Compilação = T(profile-impl) + 0

Fonte original

Conteúdo traduzido e adaptado pela redação do Notícias Mobile. Confira também a matéria na fonte original.

Leia a matéria completa