Existem duas estratégias principais para hospedar e gerenciar código através do Git: monorepo vs multi-repo. Ambas as abordagens têm os seus prós e os seus contras.

Podemos usar qualquer abordagem para qualquer base de código em qualquer linguagem. Você pode usar qualquer uma destas estratégias para projetos que contenham um punhado de bibliotecas para milhares delas. Mesmo se envolver alguns membros da equipe ou centenas, ou se você quiser hospedar código privado ou de código aberto, você ainda pode ir com monorepo ou multi-repo com base em vários fatores.

Quais são os benefícios e inconvenientes de cada abordagem? Quando devemos usar uma ou a outra? Vamos descobrir!

O que são Repos?

Um repo (abreviação para repositório) é um armazenamento para todas as alterações e arquivos de um projeto, permitindo aos desenvolvedores “controlar a versão” dos ativos do projeto ao longo de sua fase de desenvolvimento.

Normalmente nos referimos aos repositórios Git (como fornecidos pelo GitHub, GitLab ou Bitbucket), mas o conceito também se aplica a outros sistemas de controle de versão (como o Mercurial).

O que é Monorepo?

A abordagem monorepo utiliza um único repositório para hospedar todo o código das múltiplas bibliotecas ou serviços que compõem os projetos de uma empresa. No seu extremo, toda a base de código de uma empresa – abrangendo vários projetos e codificados em diferentes linguagens – é hospedada em um único repositório.

Benefícios do Monorepo

Hospedar toda a base de código em um único repositório oferece os seguintes benefícios.

Barreiras de entrada mais baixas

Quando os novos funcionários começam a trabalhar para uma empresa, eles precisam baixar o código e instalar as ferramentas necessárias para começar a trabalhar em suas tarefas. Suponha que o projeto esteja disperso por muitos repositórios, cada um tendo suas instruções de instalação e ferramentas necessárias. Nesse caso, a instalação inicial será complexa e, na maioria das vezes, a documentação não estará completa, exigindo que esses novos membros da equipe procurem os colegas para obter ajuda.

Um monorepo simplifica as coisas. Como há um único local contendo todo o código e documentação, você pode agilizar a configuração inicial.

Gestão de códigos de localização central

Ter um único repositório dá visibilidade de todo o código a todos os desenvolvedores. Ele simplifica o gerenciamento do código, uma vez que podemos usar um único rastreador de problemas para observar todos os problemas ao longo do ciclo de vida do aplicaivo.

Por exemplo, estas características são valiosas quando um problema abrange duas (ou mais) bibliotecas infantis com o bug existente na biblioteca dependente. Com vários repositórios, pode ser um desafio encontrar o pedaço de código onde o problema acontece.

Além disso, precisaríamos descobrir qual repositório usar para criar o problema e, em seguida, convidar e cruzar os membros de outras equipes para ajudar a resolver o problema.

Com um monorepo, porém, tanto a localização de problemas de código como a colaboração para solucionar problemas tornam-se mais simples de alcançar.

Refatorações para aplicativo sem dor

Ao criar uma refatoração do código em toda o aplicaivo, várias bibliotecas serão afetadas. Se você estiver hospedando-as através de múltiplos repositórios, gerenciar todos os diferentes pedidos pull para mantê-las sincronizadas umas com as outras pode se revelar um desafio.

Um monorepo facilita a realização de todas as modificações em todos os códigos para todas as bibliotecas e o envio sob um único pedido de puxar.

Funcionalidade adjacente mais difícil de ser quebrada

Com o monorepo, podemos configurar todos os testes para que todas as bibliotecas funcionem sempre que uma única biblioteca for modificada. Como resultado, a probabilidade de fazer uma alteração em algumas bibliotecas minimizou os efeitos adversos em outras bibliotecas.

As equipes compartilham uma cultura de desenvolvimento

Ainda que não impossível, com uma abordagem monorepo, torna-se um desafio inspirar subculturas únicas entre diferentes equipes. Uma vez que partilharão o mesmo repositório, muito provavelmente partilharão as mesmas metodologias de programação e gestão e utilizarão as mesmas ferramentas de desenvolvimento.

Problemas com a Abordagem Monorepo

Usar um único repositório para todos os nossos códigos tem vários inconvenientes.

Ciclos de desenvolvimento mais lentos

Quando o código de uma biblioteca contém alterações de quebra, que fazem com que os testes para bibliotecas dependentes falhem, o código também deve ser corrigido antes de fundir as alterações.

Se essas bibliotecas dependem de outras equipes, que estão ocupadas trabalhando em alguma outra tarefa e não conseguem (ou não querem) adaptar seu código para evitar as mudanças de quebra e ter os testes aprovados, o desenvolvimento da nova funcionalidade pode parar.

Além disso, o projeto pode muito bem começar a avançar apenas com a velocidade da equipe mais lenta da empresa. Esse resultado pode frustrar os membros das equipes mais rápidas, criando condições para que eles queiram deixar a empresa.

Além disso, uma biblioteca precisará executar os testes para todas as outras bibliotecas também. Quanto mais testes forem feitos, mais tempo leva para executá-los, diminuindo a velocidade com que podemos iterar o nosso código.

Requer download do codebase completo

Quando o monorepo contém todo o código de uma empresa, ele pode ser enorme, contendo gigabytes de dados. Para contribuir com qualquer biblioteca hospedada dentro, qualquer pessoa precisaria de um download de todo o repositório.

Lidar com uma vasta base de código implica um mau uso do espaço nos nossos discos rígidos e uma interacção mais lenta com ele. Por exemplo, ações cotidianas como executar o git status ou procurar na base de código com um regex podem levar muitos segundos ou até minutos mais do que levariam com múltiplos repositórios.

Bibliotecas não modificadas podem ser recém-criadas

Quando etiquetamos o monorepo, todo o código dentro dele é atribuído à nova etiqueta. Se esta ação desencadear um novo lançamento, então todas as bibliotecas hospedadas no repositório serão lançadas novamente com o número da versão da tag, mesmo que muitas dessas bibliotecas possam não ter tido nenhuma alteração.

O forking é mais difícil

Os projetos de código aberto devem facilitar ao máximo o envolvimento dos colaboradores. Com vários repositórios, os contribuidores podem ir diretamente para o repositório específico do projeto para o qual eles querem contribuir. Com um monorepo hospedando vários projetos, no entanto, os contribuidores devem primeiro navegar para o projeto certo e terão que entender como sua contribuição pode afetar todos os outros projetos.

O que é o Multi-Repo?

A abordagem multi-repo utiliza vários repositórios para hospedar as múltiplas bibliotecas ou serviços de um projeto desenvolvido por uma empresa. No seu extremo, ele irá hospedar cada conjunto mínimo de código reutilizável ou funcionalidade autônoma (como um microserviço) sob seu repositório.

Benefícios do Multi-Repo

Hospedar cada biblioteca independentemente de todas as outras proporciona uma infinidade de benefícios.

Versão de biblioteca independente

Ao etiquetar um repositório, toda a sua base de códigos é atribuída à etiqueta “novo”. Como apenas o código de uma biblioteca específica está no repositório, a biblioteca pode ser etiquetada e versionada independentemente de todas as outras bibliotecas hospedadas em outro lugar.

Ter uma versão independente para cada biblioteca ajuda a definir a árvore de dependências do aplicaivo, permitindo-nos configurar qual a versão de cada biblioteca a utilizar.

Lançamentos de serviços independentes

Como o repositório contém apenas o código para algum serviço e nada mais, ele pode ter seu próprio ciclo de implantação, independentemente de qualquer progresso feito nas aplicações que o acessam.

O serviço pode usar um ciclo de liberação rápida, como a entrega contínua (onde o novo código é implantado após passar todos os testes). Algumas bibliotecas que acessam o serviço podem usar um ciclo de liberação mais lento, como aquelas que só produzem uma nova liberação uma vez por semana.

Ajuda a definir o controle de acesso em toda a organização

Apenas os membros da equipe envolvidos no desenvolvimento de uma biblioteca precisam ser adicionados ao repositório correspondente e baixar seu código. Como resultado, há uma estratégia implícita de controle de acesso para cada camada do aplicativo. Aos envolvidos com a biblioteca serão concedidos direitos de edição, e todos os outros poderão não ter acesso ao repositório. Ou podem receber direitos de leitura, mas não de edição.

Permite que as equipes trabalhem de forma autónoma

Os membros da equipe podem projetar a arquitetura da biblioteca e implementar seu código trabalhando de forma isolada de todas as outras equipes. Eles podem tomar decisões baseadas no que a biblioteca faz no contexto geral, sem serem afetados pelos requisitos específicos de alguma equipe ou aplicaivo externa.

Problemas com a abordagem Multi-Repo

O uso de múltiplos repositórios pode dar origem a vários problemas.

As bibliotecas devem ser constantemente renovadas

Quando uma nova versão de uma biblioteca contendo alterações de quebra é lançada, as bibliotecas que dependem desta biblioteca precisarão ser adaptadas para começar a usar a versão mais recente. Se o ciclo de lançamento da biblioteca for mais rápido que o das bibliotecas dependentes, elas poderão ficar rapidamente fora de sincronia entre si.

As equipes terão de estar constantemente em dia para utilizar os últimos lançamentos de outras equipes. Dado que equipes diferentes têm prioridades diferentes, isto pode por vezes revelar-se difícil de alcançar.

Consequentemente, uma equipe que não consiga recuperar o atraso pode acabar por se agarrar à versão desatualizada da biblioteca dependente. Este resultado terá implicações no aplicaivo (em termos de segurança, velocidade e outras considerações), e a lacuna no desenvolvimento entre bibliotecas pode ser apenas maior.

Podem fragmentar as equipes

Quando equipes diferentes não precisam interagir umas com as outras, elas acabam trabalhando em seus próprios silos. A longo prazo, isto poderia levar a equipes gerando subculturas comerciais, tais como o uso de diferentes metodologias de programação ou gerenciamento, ou o uso de diferentes conjuntos de ferramentas de desenvolvimento.

Se algum membro da equipe eventualmente precisar trabalhar em uma equipe diferente, ele pode sofrer um pouco de choque cultural e aprender uma nova maneira de fazer seu trabalho.

Monorepo vs Multi-Repo: Principais diferenças

Ambas as abordagens acabam por lidar com o mesmo objetivo: gerir a base de códigos. Assim, ambas devem resolver os mesmos desafios, incluindo a gestão de versões, fomentando a colaboraçã o entre os membros da equipe, tratando de problemas, realizando testes, entre outros.

A sua principal diferença diz respeito ao momento em que os membros da equipe tomam as suas decisões: ou antecipadamente, para o monorepo, ou ao final da linha, para o multi-repo.

Vamos analisar esta ideia com mais detalhe.

Como todas as bibliotecas são versionadas independentemente no multi-reportagem, uma equipe que lança uma biblioteca com mudanças de quebra pode fazê-lo com segurança, atribuindo um novo número de versão principal ao último lançamento. Outros grupos podem ter suas bibliotecas dependentes grudadas na versão antiga e mudar para a nova, uma vez que seu código tenha sido adaptado.

Esta abordagem deixa a decisão de quando adaptar todas as outras bibliotecas a cada equipe responsável, que pode fazer isso a qualquer momento. Se o fizerem demasiado tarde e forem lançadas novas versões de bibliotecas, fechar a lacuna entre bibliotecas tornar-se-á cada vez mais difícil.

Consequentemente, enquanto uma equipe pode iterar rapidamente e frequentemente no seu código, outras equipes podem revelar-se incapazes de recuperar o atraso, acabando por produzir bibliotecas que divergem.

Por outro lado, em um ambiente monorepo, não podemos lançar uma nova versão de uma biblioteca que quebra alguma outra biblioteca, uma vez que seus testes serão reprovados. Neste caso, a primeira equipe deve comunicar com a segunda equipe para incorporar as alterações.

Esta abordagem força as equipes a adaptar todas as bibliotecas no seu conjunto sempre que uma mudança para uma única biblioteca tem de acontecer. Todas as equipes são forçadas a falar umas com as outras e a chegar a uma solução em conjunto.

Como resultado, a primeira equipe não será capaz de iterar tão rápido quanto deseja, mas o código entre diferentes bibliotecas não começará a divergir em nenhum momento.

Em resumo, a abordagem multi-repo pode ajudar a criar uma cultura de “avançar rápido e quebrar coisas” entre as equipes, onde equipes ágeis e independentes podem produzir a sua produção à sua velocidade. Em vez disso, a abordagem monorepo favorece uma cultura de consciência e cuidado, onde as equipes não devem ser deixadas para trás para lidar com um problema sozinhas.

Abordagem híbrida Poly-As-Mono

Se não podemos decidir se usamos as abordagens multi-repo ou monorepo, há também a abordagem intermediária: usar múltiplos repositórios e empregar alguma ferramenta para mantê-los sincronizados, tornando-o parecido com um monorepo, mas com mais flexibilidade.

Meta é uma dessas ferramentas. Ele organiza múltiplos repositórios sob subdiretórios e fornece uma interface de linha de comando que executa o mesmo comando em todos eles simultaneamente.

Um meta-repositório contém as informações sobre os repositórios que compõem um projeto. Clonando esse repositório via meta, todos os repositórios necessários serão clonados recursivamente, facilitando aos novos membros da equipe a começar a trabalhar em seus projetos imediatamente.

Para clonar um meta-repositório e todos os seus múltiplos repositórios definidos, devemos executar o seguinte:

meta git clone [meta repo url]

Meta irá executar um git clone para cada repositório e colocá-lo em uma subpasta:

Clonando um meta-projecto.
Clonando um meta-projecto. (Fonte da imagem: github.com/mateodelnorte/meta)

A partir daí, a execução do comando de meta exec executará o comando em cada subpasta. Por exemplo, executar o git checkout master em cada repositório é feito desta forma:

meta exec "git checkout master"

Abordagem híbrida Mono-As-Poly

Outra abordagem é gerenciar o código através de um monorepo para desenvolvimento, mas copiando o código de cada biblioteca em seu repositório independente para implantação.

Esta estratégia é predominante dentro do ecossistema PHP porque o Packagist (o repositório principal do Composer) requer uma URL de repositório público para publicar um pacote, e não é possível indicar que o pacote está localizado dentro de um subdiretório do repositório.

Dada a limitação do Packagist, os projetos PHP ainda podem usar um monorepo para desenvolvimento, mas eles devem usar a abordagem multi-repo para implantação.

Para conseguir esta conversão, podemos executar um script com git subtree split Ou usar uma das ferramentas disponíveis que executam a mesma lógica:

Quem está usando Monorepo vs Multi-Repo

Várias grandes empresas de tecnologia favorecem a abordagem monorepo, enquanto outras decidiram usar o método multi-repo.

Google, Facebook, Twitter e Uber têm todos garantido publicamente a abordagem monorepo. A Microsoft executa o maior monorepo Git do planeta para hospedar o código fonte do sistema operacional Windows.

No lado oposto, Netflix, Amazon, e Lyft são empresas famosas que utilizam a abordagem multi-repo.

No lado híbrido poly-as-mono, o Android atualiza múltiplos repositórios, que são geridos como um monorepo.

No lado híbrido mono-as-poly, Symfony mantém o código para todos os seus componentes em um monorepo. Eles o dividem em repositórios independentes para implantação (como symfony/dependency-injectionsymfony/event-dispatcher.)

Exemplos de Monorepo e Multi-Repo

A conta WordPress no GitHub hospeda exemplos de abordagens monorepo e multi-repo.

O Gutenberg, o editor de blocos do WordPress, é composto por várias dezenas de pacotes JavaScript. Estes pacotes estão todos hospedados no WordPress/gutenbergmonorepo e geridos através do Lerna para os ajudar a publicá-los no repositório npm.

Openverse, o mecanismo de pesquisa de mídia abertamente licenciado, hospeda suas principais partes em repositórios independentes: Front-end, Catálogo, e API.

Monorepo vs Multi-Repo: Como escolher?

Como acontece com muitos problemas de desenvolvimento, não há uma resposta pré-definida sobre qual abordagem você deve usar. Diferentes empresas e projectos irão beneficiar de uma estratégia ou de outra com base nas suas condições únicas, como por exemplo:

  • Qual é o tamanho da base de código? Ela contém gigabytes de dados?
  • Quantas pessoas vão trabalhar na base de código? São cerca de 10, 100, ou 1.000?
  • Quantos pacotes haverá? São cerca de 10, 100, ou 1.000?
  • Quantos pacotes a equipe precisa trabalhar em um determinado momento?
  • Quão apertados são os pacotes?
  • Estão envolvidas diferentes linguagens de programação? Requerem um software específico instalado ou hardware especial para funcionar?
  • Quantas ferramentas de implantação são necessárias, e qual a complexidade da sua instalação?
  • Qual é a cultura da empresa? As equipes são encorajadas a colaborar?
  • Que ferramentas e tecnologias as equipes sabem utilizar?

Resumo

Há duas estratégias principais para hospedar e gerenciar código: monorepo vs multi-repo. A abordagem monorepo implica armazenar o código para diferentes bibliotecas ou projetos – e mesmo todo o código de uma empresa – em um único repositório. E o sistema multi-repo divide o código em unidades, tais como bibliotecas ou serviços, e mantém o seu código hospedado em repositórios independentes.

Qual a abordagem a utilizar depende de uma multiplicidade de condições. Ambas as estratégias têm várias vantagens e desvantagens, e nós acabamos de cobrir todas elas em detalhe neste artigo.

Ainda tem perguntas sobre monorepos ou multi-repos? Escreva-nos na seção de comentários!

Leonardo Losoviz

Leo writes about innovative web development trends, mostly concerning PHP, WordPress and GraphQL. You can find him at leoloso.com and twitter.com/losoviz.