Monorepo vs polyrepo: mantendo web, mobile e backend em sincronia
Monorepo vs polyrepo explicado para equipes que entregam apps web, mobile e backend. Compare dependências, coordenação de releases e táticas de CI para manter a velocidade.

O problema real: entregar mudanças em três bases de código
Equipes não discutem monorepo vs polyrepo por gosto de filosofia do Git. A discussão aparece porque uma pequena mudança de produto vira três mudanças separadas no web, mobile e backend, e algo quebra no caminho.
O que quebra primeiro raramente é a interface. Normalmente é a cola invisível: um contrato de API mudou sem a atualização correspondente, uma biblioteca compartilhada foi atualizada em um lugar mas não em outro, ou o pipeline de build de repente precisa de um novo passo. Quando uma peça é entregue antes das outras, os usuários sentem como bugs do tipo “o botão aparece no web mas o app mobile diz não suportado” ou “o app fica carregando para sempre porque a resposta do backend mudou.”
Web, mobile e backend também funcionam em relógios de release diferentes. Web pode ser lançado várias vezes ao dia. Backend também pode ser frequente, mas precisa de rollout cuidadoso. Mobile é o mais lento porque revisão nas lojas e atualizações dos usuários adicionam atraso real. Uma mudança “simples” como renomear um campo pode te forçar a planejar em torno da pista mais lenta, mesmo que apenas uma tela precise da mudança.
Você provavelmente está pagando um imposto de coordenação de repositório se isso continuar acontecendo:
- Mudanças quebrando a API são descobertas depois do merge.
- Alinhamento de versões depende de lembretes manuais e planilhas.
- Uma feature precisa de múltiplos pull requests coordenados que ficam aguardando uns aos outros.
- CI está lento porque constrói e testa muito mais do que a mudança tocou.
- Rollbacks parecem arriscados porque não está claro qual commit corresponde a qual release.
Tamanho da equipe e maturidade do produto mudam a resposta certa. No começo, a maioria das equipes vence ao tornar a coordenação barata e a visibilidade alta, mesmo que as coisas fiquem um pouco bagunçadas. À medida que crescem, limites começam a importar, mas só se interfaces forem estáveis e a propriedade clara.
Se toda mudança significativa precisa aterrissar em três lugares, você pagará esse imposto de alguma forma. A estratégia de repositório é basicamente sobre como você quer pagar.
Monorepo e polyrepo sem jargões
Um repositório é só onde seu código vive, junto com seu histórico. Quando você tem web, mobile e backend, a escolha é direta: manter tudo junto, ou dividir.
Um monorepo é um repositório que contém múltiplos apps e frequentemente código compartilhado também. Web, iOS/Android, serviços backend e bibliotecas compartilhadas ficam lado a lado.
Um polyrepo é o oposto: cada app (e às vezes cada serviço) tem seu próprio repositório. Código compartilhado geralmente vira um pacote separado, ou equipes copiam pedaços pequenos quando necessário.
No dia a dia, monorepos normalmente parecem assim: compartilhar código é fácil, mudanças entre apps podem ser um único pull request, e as regras são consistentes. A troca é social: propriedade pode ficar confusa a menos que você estabeleça limites claros, e checagens em todo o repositório podem parecer rígidas.
Polyrepos geralmente parecem assim: cada equipe pode se mover de forma independente, repositórios ficam focados, e controle de acesso pode ser mais simples. A troca é coordenação: compartilhar código exige planejamento, e mudanças entre apps geralmente viram múltiplos pull requests com timing cuidado.
Muitas equipes acabam com um híbrido: apps em repositórios separados, contratos compartilhados em um lugar; ou um monorepo com limites fortes para que cada equipe permaneça majoritariamente em sua área.
Se você usa uma plataforma que gera backend, web e mobile a partir de uma única fonte de verdade, você reduz deriva porque contratos e lógica vivem juntos. AppMaster (appmaster.io), por exemplo, gera backend pronto para produção, web e apps nativos móveis a partir de um único modelo. Isso não elimina as realidades de release (mobile ainda libera mais devagar), mas pode eliminar muita da sobrecarga “a gente atualizou os três lugares?”.
Gestão de dependências: manter o código compartilhado seguro
Código compartilhado é onde as equipes perdem tempo, independentemente da organização dos repositórios. Uma pequena mudança numa biblioteca compartilhada ou contrato de API pode quebrar builds web, releases mobile e deploys backend de maneiras diferentes.
Quando você compartilha bibliotecas (componentes UI, regras de validação, helpers de autenticação), você escolhe entre uma versão para todos ou múltiplas versões ao longo do tempo.
- Uma versão é mais simples e evita surpresas de “funciona no meu branch”.
- Múltiplas versões deixam equipes seguirem no seu ritmo, mas criam acúmulo e tornam correções de segurança mais difíceis de aplicar.
Clientes de API e esquemas merecem cuidado extra. Atualizações manuais são lentas e propensas a erro. Um padrão melhor é tratar o esquema da API como fonte da verdade e gerar clientes a partir dele, depois ou checá-los no repositório ou gerá-los no CI. O objetivo é falhar rápido: se o backend adiciona um campo obrigatório, o cliente mobile deve quebrar na build, não três dias depois em QA.
Mudanças quebrando espalham-se quando o comportamento muda sem um caminho seguro adiante. Prefira mudanças aditivas primeiro (novos campos, novos endpoints) e depois depreque. Se for preciso quebrar, use endpoints versionados ou uma janela curta de compatibilidade.
Exemplo concreto: o backend renomeia status para state. Se o web atualiza hoje mas o mobile não pode liberar por uma semana, o backend precisa aceitar ambos os campos nessa semana, ou entregar um adaptador que mapeie o campo antigo para o novo.
Algumas regras que mantêm atualizações de dependência monótonas (no bom sentido):
- Atualize com cadência. Pequenas atualizações semanais vencem grandes trimestrais.
- Exija aprovação explícita para mudanças quebrantes, mais uma nota curta de migração.
- Automatize checagens: atualizações de dependência, regeneração de clientes e testes básicos de contrato.
- Defina “pronto” como “web, mobile e backend builds estão verdes”, não “meu repositório passa”.
Código gerado pode reduzir deriva, mas não substitui disciplina. Você ainda precisa de um contrato único, deprecações claras e atualizações previsíveis.
Coordenação de releases: alinhar web, mobile e backend
Coordenação de releases é onde a estratégia de repositório deixa de ser teórica. Se o backend muda um campo de API, o app web costuma atualizar e liberar no mesmo dia. Apps mobile são diferentes: revisão nas lojas e tempo de atualização dos usuários podem transformar um pequeno descompasso em uma semana de tickets de suporte.
O objetivo prático é simples: uma ação do usuário deve funcionar independentemente de qual parte atualizou primeiro. Isso significa planejar para versões mistas, não assumir um release perfeitamente sincronizado.
Padrões de versionamento que equipes realmente usam
A maioria das equipes se ajeita em uma dessas abordagens:
-
Um trem de releases compartilhado: web, mobile e backend são entregues como uma unidade versionada.
-
Versões por serviço com regras de compatibilidade: cada app/serviço tem sua própria versão, e o backend suporta uma faixa definida de versões de cliente.
Um trem de release compartilhado parece arrumado, mas frequentemente se quebra por atrasos em mobile. Versões por serviço são mais confusas no papel, mas combinam com a realidade. Se você optar por versões por serviço, escreva uma regra e faça cumprir: quais versões do backend devem suportar quais versões mobile, e por quanto tempo.
Atrasos em mobile também mudam como você lida com hotfixes. Hotfixes no backend podem ir rápido; hotfixes mobile podem não alcançar usuários por dias. Priorize correções server-side que mantenham builds mobile antigos funcionando. Quando for preciso mudar o cliente, use feature flags e evite remover campos antigos até ter certeza de que a maioria dos usuários atualizou.
Exemplo: você adiciona “instruções de entrega” ao fluxo de pedido. O backend adiciona um campo opcional, o web exibe de imediato, e o mobile exibe na próxima sprint. Se o backend aceitar requisições antigas e manter o campo opcional, tudo continua funcionando enquanto o mobile alcança a atualização.
Quem cuida do calendário de releases
A coordenação falha quando “todo mundo é dono” e portanto ninguém é. O dono pode ser um tech lead, um release manager, ou um product manager com forte suporte de engenharia. O trabalho dele é evitar surpresas mantendo expectativas de release visíveis e consistentes.
Não precisa de processo complexo. Precisa de hábitos repetíveis: um calendário simples de releases com cutoffs e janelas de freeze, uma checagem rápida entre equipes antes de mudanças de API serem publicadas, e um plano claro do que fazer quando o mobile atrasa (segurar o backend vs manter compatibilidade).
Se seu fluxo gera web, mobile e backend juntos a partir de um mesmo modelo, você ainda precisa de um dono de release. Normalmente haverá menos momentos de “atualizamos os três lugares?”, mas o timing do mobile persiste.
Manter o tempo de CI sob controle
CI fica lento pelas mesmas razões em ambos os setups: você reconstrói demais, reinstala dependências repetidamente e roda todos os testes em toda mudança.
Matadores de tempo comuns incluem builds completos em mudanças pequenas, caches ausentes, suítes de teste que rodam tudo, e jobs seriais que poderiam correr em paralelo.
Comece com melhorias que ajudam em qualquer cenário:
- Faça cache do download de dependências e dos outputs de build.
- Rode lint, testes unitários e builds em paralelo quando possível.
- Separe checagens rápidas (todo commit) das checagens mais lentas (branch main, nightly ou pre-release).
Táticas de monorepo que ajudam
Monorepos ficam dolorosos quando cada commit dispara um pipeline “construa o mundo”. A correção é construir e testar apenas o que foi afetado pela mudança.
Use filtros por caminho e uma abordagem apenas-afetada: se você alterou código de UI mobile, não reconstrua imagens de backend. Se tocou uma biblioteca compartilhada, construa e teste apenas os apps que dependem dela. Muitas equipes formalizam isso com um grafo de dependências simples para que o CI decida ao invés de chutar.
Táticas de polyrepo que evitam deriva
Polyrepos podem ser rápidos porque cada repo é menor, mas frequentemente desperdiçam tempo por duplicação e ferramentas inconsistentes.
Mantenha um conjunto compartilhado de templates de CI (mesmos passos, mesmos caches, mesmas convenções) para que cada repositório não reimplemente o pipeline. Trave toolchains (versões de runtime, ferramentas de build, linters) para evitar surpresas de “funciona num repositório”. Se o download de dependências for gargalo, configure caches compartilhados ou mirrors internos para que cada repo não baixe tudo do zero.
Exemplo concreto: uma feature adiciona um novo campo “status”. Backend muda, web mostra, mobile mostra. Em um monorepo, o CI deve rodar testes do backend mais apenas as partes de web e mobile que dependem do cliente de API. Em um setup polyrepo, cada repositório deve rodar suas próprias checagens rápidas, e um pipeline de integração separado pode validar que os três releases ainda concordam.
Se você exporta código fonte e roda seu próprio CI, a mesma regra se aplica: construa só o que mudou, reaproveite caches agressivamente, e reserve checagens lentas para quando elas agregam valor real.
Passo a passo: escolha a estratégia de repositório que cabe na sua equipe
A decisão fica mais fácil quando você parte do dia a dia ao invés da ideologia.
1) Anote o que precisa mudar junto
Escolha 5 a 10 features recentes e anote o que precisou se mover em conjunto. Marque se cada uma tocou telas UI, endpoints de API, tabelas de dados, regras de autenticação ou validações compartilhadas. Se a maioria das features exige mudanças coordenadas nas três áreas, um setup dividido vai parecer doloroso a não ser que seu processo de release seja muito disciplinado.
2) Trace código e decisões compartilhadas
Código compartilhado não são só bibliotecas. São também contratos (esquemas de API), padrões de UI e regras de negócio. Anote onde eles vivem hoje, quem os edita e como mudanças são aprovadas. Se pedaços compartilhados são copiados entre repositórios, é um sinal de que você precisa de controle mais rígido, seja através de um monorepo ou regras de versionamento estritas.
3) Defina limites e donos
Decida quais são as unidades (apps, serviços, bibliotecas) e depois atribua um dono para cada unidade. Limites importam mais que a disposição dos repositórios. Sem donos, um monorepo vira barulhento. Sem donos, um polyrepo vira desconectado.
Se quiser um checklist simples, mire em: um repositório ou pasta por serviço/app deployável, um lugar para contratos compartilhados, um lugar para componentes de UI realmente compartilhados, uma regra clara de onde vive a lógica de negócio, e um dono documentado para cada um.
4) Escolha um modelo de release que vocês consigam seguir
Se releases mobile atrasam, você precisa de um plano de compatibilidade (APIs versionadas, campos backward-compatible ou uma janela de suporte definida). Se tudo precisa ser lançado junto, um trem de releases pode funcionar, mas aumenta a coordenação.
Mantenha regras de branching entediantes: branches de curta duração, merges pequenos e um caminho claro para hotfixes.
5) Projete CI ao redor das mudanças comuns
Não projete CI para o pior caso no dia 1. Projete para o que as pessoas fazem no dia a dia.
Se a maioria dos commits toca apenas UI web, rode lint e testes unitários web por padrão e rode testes end-to-end completos em uma agenda ou antes de releases. Se a maioria dos incidentes vem de drift de API, invista primeiro em testes de contrato e geração de clientes.
Exemplo: uma feature que toca web, mobile e backend
Imagine uma pequena equipe construindo três coisas ao mesmo tempo: um portal cliente (web), um app de campo (mobile) e uma API (backend). Surge um pedido: adicionar um novo campo “Service status” aos jobs e mostrá-lo em todo lugar.
A mudança parece pequena, mas é um teste de coordenação. Backend adiciona o campo e atualiza validações e respostas. Web exibe e atualiza filtros. Mobile precisa exibir offline, sincronizar e tratar casos de borda.
Agora o problema real: a mudança na API é quebrante. O nome do campo troca de status para service_status, e clientes antigos travam se não lidarem com isso.
O que um monorepo muda
Aqui um monorepo costuma parecer mais calmo. Backend, web e mobile podem aterrissar num único pull request (ou um conjunto coordenado de commits). O CI pode rodar testes afetados, e você pode marcar um release que inclui todas as três atualizações.
O risco principal é social, não técnico: um repositório único facilita mesclar uma mudança quebrante rapidamente, então suas regras de revisão precisam ser fortes.
O que um polyrepo muda
Com repositórios separados, cada app vive em seu próprio calendário. O backend pode liberar primeiro, e web e mobile correm para alcançar. Se releases mobile exigem revisão na loja, o “conserto” pode levar dias mesmo que a mudança de código seja pequena.
Equipes normalmente resolvem isso com mais estrutura: endpoints versionados, respostas backward-compatible, janelas de deprecação mais longas e passos de rollout claros. Funciona, mas é trabalho contínuo.
Se você decide por evidências, olhe para os últimos meses:
- Se incidentes frequentemente vêm de versões desencontradas, prefira coordenação mais apertada.
- Se releases são frequentes e sensíveis ao tempo (especialmente mobile), evite mudanças quebrantes ou centralize-as.
- Se equipes são independentes e raramente tocam a mesma feature, o overhead do polyrepo pode valer a pena.
Erros comuns e armadilhas a evitar
A maioria das equipes não falha por escolher a estrutura “errada” de repositório. Falham porque hábitos do dia a dia adicionam atrito lentamente até que toda mudança pareça arriscada.
Código compartilhado vira depósito
Uma biblioteca compartilhada é tentadora: helpers, tipos, pedaços de UI, gambiarras “temporárias”. Logo vira o lugar onde código antigo se esconde, e ninguém sabe o que é seguro mudar.
Mantenha código compartilhado pequeno e rigoroso. “Compartilhado” deve significar usado por muitas equipes, revisado cuidadosamente e alterado com intenção.
Acoplamento forte por suposições ocultas
Mesmo em repositórios separados, sistemas podem ficar fortemente acoplados. O acoplamento apenas se muda para suposições: formatos de data, valores de enums, regras de permissão e “este campo sempre existe”.
Exemplo: mobile trata status = 2 como “Aprovado”, web trata como “Confirmado”, backend muda a ordem dos enums e tudo quebra de uma forma que parece aleatória.
Previna isso documentando contratos (o que os campos significam, quais valores são permitidos) e tratando-os como regras de produto, não trivia.
Propriedade confusa
Quando todo mundo pode mudar qualquer coisa, reviews ficam rasos e erros passam. Quando ninguém é dono, bugs ficam semanas parados.
Defina donos para web, mobile, backend e módulos compartilhados. Proprietário não bloqueia contribuições; assegura que mudanças recebam os olhos certos.
CI cresce sem poda
CI costuma começar pequeno, depois cada incidente adiciona um job “só para garantir”. Meses depois está lento e caro, e as pessoas evitam rodá-lo.
Uma regra simples ajuda: todo job de CI precisa de propósito claro e um dono, e deve ser removido se parar de pegar problemas reais.
Sinais de alerta incluem testes duplicados entre jobs, jobs que ficam vermelhos por dias, “mudanças rápidas” que demoram mais para verificar do que para construir, e pipelines que disparam builds mobile em mudanças apenas de backend.
Coordenação de releases baseada em conhecimento tribal
Se releases dependem de uma pessoa lembrando a ordem certa e os truques secretos, vocês vão entregar mais devagar e quebrar mais coisas.
Escreva os passos de release, torne-os repetíveis e automatize checagens chatas. Mesmo que sua ferramenta gere backends e clientes consistentes, você ainda precisa de regras claras de release.
Checagens rápidas antes de se comprometer com monorepo ou polyrepo
Antes de reorganizar repositórios, valide como sua equipe entrega hoje. O objetivo não é uma estrutura perfeita. É menos surpresas quando uma mudança toca web, mobile e backend.
Faça cinco perguntas:
- Envio independente: Você pode liberar um conserto de backend sem forçar uma atualização do app mobile no mesmo dia?
- Regras de mudança de API: Existe um contrato escrito para deprecações e por quanto tempo o comportamento antigo fica suportado?
- Disciplina de código compartilhado: Bibliotecas compartilhadas (componentes UI, clientes de API, regras de negócio) são revisadas e versionadas consistentemente?
- CI que roda o que importa: O CI consegue identificar o que mudou e rodar builds/testes só nas partes afetadas?
- Visão única de releases: Há um lugar único para ver o que está saindo em web, mobile e backend, com donos e datas?
Exemplo simples: um novo campo “endereço” é adicionado ao checkout. Se o backend libera primeiro, o app mobile antigo deve continuar funcionando. Isso normalmente significa que a API aceita tanto payloads antigos quanto novos por um tempo, e atualizações do cliente são opcionais, não obrigatórias.
Próximos passos: reduzir trabalho de coordenação e entregar com confiança
O objetivo não é a estrutura de repositório “certa”. É menos handoffs, menos surpresas e menos momentos de “pera, qual versão está no ar?”.
Escreva um registro curto da decisão: por que escolheram a abordagem atual, o que esperam melhorar e quais tradeoffs estão aceitando. Reavalie a cada 6 a 12 meses, ou antes se o tamanho da equipe ou o ritmo de releases mudar.
Antes de mover arquivos, escolha a menor mudança que remove dor real:
- Adote e siga regras de versionamento para pacotes compartilhados.
- Defina contratos de API e aplique testes de contrato no CI.
- Combine um checklist de release entre web, mobile e backend.
- Use ambientes de preview para mudanças que tocam várias partes.
- Estabeleça orçamentos de tempo de CI (por exemplo: checagens de PR abaixo de 15 minutos).
Se o acoplamento entre bases de código é o gargalo real, reduzir o número de lugares que precisam mudar pode importar mais que a disposição dos repositórios. Algumas equipes fazem isso movendo mais lógica e modelagem de dados para uma única fonte de verdade.
Se quiser explorar essa abordagem, AppMaster (appmaster.io) foi construído para gerar serviços backend, apps web e mobile nativos com modelos de dados e lógica de negócio compartilhados. Uma maneira de avaliar com baixo risco é construir uma pequena ferramenta interna primeiro e decidir com base em quanto trabalho de coordenação isso remove.
O caminho confiante é propositalmente chato: documente a decisão, reduza acoplamento, automatize checagens e mude a estrutura de repositórios só quando os números mostrarem que vai ajudar.
FAQ
Comece por analisar com que frequência uma única funcionalidade exige mudanças no web, mobile e backend. Se a maior parte do trabalho é transversal e a coordenação é seu principal problema, um monorepo ou uma abordagem de “contrato único” tende a reduzir quebras. Se as equipes raramente mexem nas mesmas áreas e precisam de controle de acesso e releases independentes, o polyrepo pode funcionar bem com regras rígidas de compatibilidade.
Desvio de API, incompatibilidade de versões de bibliotecas compartilhadas e diferenças no tempo de release (especialmente a demora de lojas de apps) são os culpados mais comuns. A solução é planejar versões mistas na prática, não depender de releases perfeitamente sincronizados, e tornar mudanças quebras raras e deliberadas.
Trate o esquema da API como fonte da verdade e gere clientes a partir dele para que incompatibilidades quebrem na build, não em QA ou produção. Prefira mudanças aditivas primeiro, depois depreque campos antigos; mantenha uma janela curta de compatibilidade quando for preciso renomear ou remover algo.
Aposte em atualizações pequenas e regulares (uma cadência semanal vence um acúmulo trimestral) e exija aprovação explícita para mudanças quebras. Inclua uma nota curta de migração em cada mudança e defina “pronto” como “web, mobile e backend builds estão verdes”, não apenas um repositório passando.
A opção padrão costuma ser versões por serviço com uma regra clara de compatibilidade: quais versões do backend suportam quais versões dos clientes e por quanto tempo. Um trem de releases único pode funcionar no início, mas atrasos de mobile frequentemente o tornam doloroso, a menos que seu produto tolere esperar pela loja de apps.
Mantenha o backend compatível para que as versões antigas do mobile continuem funcionando enquanto os usuários atualizam. Use campos aditivos, não remova comportamentos antigos cedo demais, e utilize feature flags para liberar mudanças visíveis ao cliente gradualmente.
Deixe essa responsabilidade explícita—frequentemente um tech lead, um release manager ou um product owner com suporte de engenharia. O objetivo é um calendário simples e repetível e uma regra clara para atrasos (segurar a mudança vs manter compatibilidade), não um processo pesado.
Construa e teste só o que mudou por padrão e faça cache de tudo que puder. Separe checagens rápidas (a cada commit) de checagens lentas (branch main, nightly ou pre-release) para dar feedback rápido sem pagar todo o custo de teste sempre.
Use filtros por caminho e uma abordagem “apenas o afetado” para evitar rebuilds globais por mudanças pequenas. Se um módulo compartilhado mudar, rode checagens só para os apps que dependem dele, e mantenha regras de propriedade e revisão claras para que o repositório não vire depósito de código sem dono.
Padronize ferramentas e templates de CI entre os repositórios para evitar reinventar passos, caches e convenções. Adicione uma checagem de integração que valide contratos-chave entre releases e fixe versões das toolchains para evitar “funciona num repo e quebra no outro”.


