Pular para o conteúdo

Monorepo e polirrepo

Avançado16 min de leituraversionamento

Monorepo é um repositório com vários projetos que compartilham histórico, versão e processo. Não é “todo o código da empresa num lugar só” — é uma decisão sobre onde fica a fronteira de coordenação. E, como toda fronteira, ela move o custo em vez de eliminá-lo.

Monorepo Polirrepo
Mudança que atravessa serviços Um PR, atômico Vários PRs, ordem e coordenação
Reuso de biblioteca interna Import direto, sempre atualizado Publicar versão, atualizar consumidores
Refatoração ampla Viável (ferramenta vê tudo) Cara, feita em ondas
CI Precisa de build afetado Simples e naturalmente isolado
Permissão por time Por caminho (CODEOWNERS) Nativa, por repositório
Autonomia de versões Baixa: todos no mesmo tronco Alta: cada um no seu ritmo
Ferramentas Git Sofrem com repositório grande Sem problema

O ganho central do monorepo é a mudança atômica: alterar o contrato e todos os consumidores no mesmo commit, com o CI verificando o conjunto. O custo central é que você precisa construir a infraestrutura que o polirrepo dá de graça — build afetado, isolamento de permissão, ferramenta de release.

Rodar tudo a cada commit é o que faz monorepo ter fama de CI lento. A saída é calcular o grafo de dependências e executar só o que a mudança alcança.

Janela do terminal
# Nx: o que mudou em relação ao tronco, e o que depende disso
npx nx affected -t build,test,lint --base=origin/main
# Turborepo: com cache remoto, o que não mudou nem executa
npx turbo run build test --filter='...[origin/main]'

O que faz isso funcionar de verdade:

  • Cache remoto compartilhado entre CI e máquinas locais — a maior economia isolada.
  • Tarefas determinísticas: mesma entrada, mesma saída. Passo que lê data, rede ou estado global destrói o cache.
  • Fronteiras declaradas: cada projeto declara suas dependências; import fora do grafo quebra o cálculo do afetado e precisa ser barrado por lint.

Sem affected e cache, o monorepo cobra o preço do acoplamento sem entregar a velocidade.

  • Turborepo — simples, cache remoto bom, ótimo para JavaScript/TypeScript.
  • Nx — grafo rico, geradores, análise de fronteiras; mais opinativo.
  • Bazel / Pants — reprodutibilidade forte e suporte poliglota, para escala grande; curva alta e custo de manutenção real.
  • Changesets — versionamento e changelog de pacotes dentro do monorepo.
  • Para repositório muito grande, o Git tem sparse-checkout e clone parcial (--filter=blob:none), que resolvem a lentidão local.

Comece pelo mais simples que resolve. Bazel é excelente e é um projeto de plataforma; não é o primeiro passo de ninguém.

jobs:
afetados:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # o cálculo precisa do histórico
- run: npx turbo run lint test build --filter='...[origin/main]'

Além do afetado e do cache, três ajustes cortam a maior parte do tempo restante: paralelizar por projeto em matriz, separar verificação rápida (lint, tipos, unitário) de verificação lenta (integração, e2e) em etapas distintas, e aplicar merge queue para o tronco não quebrar com merges concorrentes.

E um detalhe de deploy: em monorepo, o gatilho de deploy precisa ser por caminho alterado, ou todo merge publica tudo.

Nem sempre a resposta é juntar tudo. Divida quando:

  • O ciclo de vida é genuinamente independente (produto de terceiro, ferramenta interna isolada).
  • A exigência de permissão é forte — código com restrição de acesso não deve ficar no mesmo repositório de todo mundo.
  • A stack não compartilha ferramenta nenhuma e a “vantagem” seria só a pasta comum.
  • Open source: o público precisa ver aquele projeto, e não o resto.

E o meio-termo mais comum na prática brasileira funciona bem: um monorepo por domínio ou por time, com bibliotecas compartilhadas publicadas por versão. Você fica com mudanças atômicas onde elas acontecem de verdade — dentro do domínio — sem construir plataforma de build para a empresa inteira.

Qualquer que seja a escolha, o que separa o resultado bom do ruim não é a topologia: é ter CI rápido, fronteiras explícitas e uma forma clara de saber quem é dono do quê.

Próximo passo: você fechou a trilha de Versionamento. Leve isso ao pipeline em Anatomia de um pipeline e acelere a verificação com Acelerar pipeline lento.