Olá pessoal!
Então, uma nova parte do holivar prometido sobre monorepositórios. Na primeira parte, discutimos a tradução de um artigo de um respeitado engenheiro da Lyft (e anteriormente do Twitter) sobre quais são as desvantagens dos mono-repositórios e por que elas nivelam quase todas as vantagens dessa abordagem. Pessoalmente, concordo amplamente com os argumentos apresentados no artigo original. Mas, como prometido, para encerrar essa discussão, gostaria de expressar mais alguns pontos, na minha opinião ainda mais importantes e mais práticos.
Vou falar um pouco sobre mim - trabalhei em projetos pequenos e relativamente grandes, usei poli-repositórios em um projeto com mais de 100 microsserviços (e SLA 99,999%). No momento, estou envolvido na tradução de um pequeno repositório mono (na verdade não, apenas o front-end js + java) de maven para bazel. Não funcionou no Google, Facebook, Twitter, ou seja, Não tive o prazer de usar um repositório mono devidamente configurado e ajustado.
Então, para iniciantes, o que é um monorepositório? Comentários sobre a tradução do artigo original mostraram que muitos acreditam que um mono-repositório é quando todos os 5 desenvolvedores da empresa trabalham em um repositório e armazenam o front-end e o back-end juntos. Claro, isso não é verdade. Um mono-repositório é uma maneira de armazenar todos os projetos da empresa, bibliotecas, ferramentas de construção, plug-ins IDE, scripts de implantação e tudo o mais em um grande repositório. Os detalhes aqui são
trunkbaseddevelopment.com .
Como é chamada a abordagem quando a empresa é pequena e simplesmente não possui tantos projetos, módulos, componentes? Este também é um monorepositório, apenas um pequeno.
Naturalmente, o artigo original diz que todos os problemas descritos começam a aparecer em uma determinada escala. Portanto, aqueles que escrevem que seu repositório mono de 1.5 digger funciona perfeitamente certamente estão absolutamente certos.
Então, o primeiro fato que eu gostaria de corrigir: um
monorepositório é um ótimo começo para o seu novo projeto . Colocando todo o código em uma pilha, primeiro você terá apenas uma vantagem, porque o suporte a vários repositórios certamente adicionará um pouco de sobrecarga.
Qual é o problema então? E o problema, como observado no artigo original, começa em uma certa escala. E o mais importante, não perca o momento em que essa escala já chegou.
Portanto, estou inclinado a afirmar que, em essência, os problemas que surgem não são os da abordagem "coloque todo o seu código em um heap", mas esses são problemas de simplesmente grandes repositórios de código-fonte. I.e. supondo que você usou poli-repositórios para diferentes serviços / componentes, e um desses serviços se tornou tão grande (quão grande, discutiremos um pouco mais adiante), é provável que você tenha exatamente os mesmos problemas, mas também sem as vantagens dos mono-repositórios (se eles Claro que existe).
Então, qual o tamanho do repositório para começar a ser considerado problemático?
Definitivamente, existem 2 indicadores dos quais isso depende - a quantidade de código e o número de desenvolvedores que trabalham com esse código. Se o seu projeto tiver terabytes de código, mas 1-2 pessoas trabalharem com ele, provavelmente elas quase não perceberão problemas (bem, ou pelo menos será mais fácil não fazer nada, mesmo que notem :)
Como determinar que é hora de pensar em como melhorar seu repositório? Obviamente, esse é um indicador subjetivo, provavelmente seus desenvolvedores começarão a reclamar que algo não lhes convém. Mas o problema é que pode ser tarde demais para mudar alguma coisa. Deixe-me dar alguns dados pessoais: se a clonagem de seu repositório demorar mais de 10 minutos, se a construção de um projeto demorar mais de 20 a 30 minutos, se o número de desenvolvedores exceder 50 e assim por diante.
Um fato interessante da prática pessoal:Eu trabalhei em um monólito bastante grande em uma equipe de cerca de 50 desenvolvedores, divididos em várias equipes pequenas. O desenvolvimento foi realizado em brunches de recursos e a mesclagem ocorreu pouco antes do congelamento do recurso. Uma vez, passei três dias na fusão do ramo de nossa equipe, depois que outras seis equipes congelaram na minha frente.
Agora vamos examinar a lista dos problemas que surgem em grandes repositórios (alguns deles foram mencionados no artigo original, outros não).
1) Tempo de download do repositório
Por um lado, podemos dizer que esta é uma operação única que o desenvolvedor executa durante a configuração inicial de sua estação de trabalho. Pessoalmente, muitas vezes tenho situações em que quero clonar um projeto em uma pasta vizinha, aprofundá-lo e excluí-lo. No entanto, se a clonagem demorar mais de 10 a 20 minutos, isso não será tão conveniente.
Além disso, não esqueça que, antes de montar o projeto no servidor de IC, é necessário clonar o repositório para cada agente de construção. E aqui você começa a descobrir como economizar esse tempo, porque se cada montagem demorar 10 a 20 minutos mais e o resultado da montagem aparecer 10 a 20 minutos depois, isso não será adequado para ninguém. Portanto, o repositório começa a aparecer nas imagens das máquinas virtuais das quais os agentes são implantados, complexidade adicional e custos adicionais para dar suporte a esta solução.
2) Tempo de construção
Este é um ponto bastante óbvio que já foi discutido várias vezes. De fato, se você tiver muitos códigos-fonte, a montagem, em qualquer caso, levará um tempo considerável. Uma situação familiar é quando, após alterar uma linha de código, é necessário aguardar meia hora até que as alterações sejam remontadas e testadas. De fato, existe apenas uma saída: usar um sistema de compilação criado com base em resultados de cache e compilações incrementais.
Não há muitas opções aqui - apesar de os recursos de armazenamento em cache terem sido adicionados ao mesmo gradle (infelizmente, eu não os usei na prática), eles não trazem benefícios práticos devido ao fato de os sistemas de compilação tradicionais não terem resultados repetíveis. (construções reproduzíveis). I.e. de qualquer maneira, devido aos efeitos colaterais da compilação anterior, em algum momento será necessário chamar a limpeza de cache (a abordagem padrão de
maven clean build
). Portanto, resta apenas a opção de usar o Bazel / Buck / Pants e outros como eles. Por que isso não é muito bom, discutiremos um pouco mais tarde.
3) IDE de indexação
Meu projeto atual é indexado no Intellij IDEA por 30 a 40 minutos. E o seu? Obviamente, você pode abrir apenas parte do projeto ou excluir todos os módulos desnecessários da indexação, mas ... O problema é que a reindexação ocorre toda vez que você alterna de uma ramificação para outra. É por isso que eu gosto de clonar um projeto em um diretório vizinho. Algumas pessoas começam a armazenar em cache o cache do IDE :)
<Foto de DiCaprio com olhos estreitados>
4) Criar logs
Qual servidor de CI você está usando? Ele fornece uma interface conveniente para exibir e navegar por vários gigabytes de logs de construção? Infelizmente o meu não é :(
5) Histórico de confirmações
Você gosta de assistir ao histórico de confirmação? Eu amo, especialmente em uma ferramenta com uma interface gráfica (percebo melhor as informações visualmente, não repreendo :).
É assim que o histórico de consolidação se parece no meu repositório Você gosta disso? É conveniente? Pessoalmente, eu não!
6) testes quebrados
O que acontece se alguém conseguiu executar testes quebrados / código não compilador no mestre? Você certamente dirá que seu IC não permite que você faça isso. E os testes instáveis que o autor passa, e mais ninguém? Agora imagine que esse código se espalhou para as máquinas de 300 desenvolvedores, e nenhum deles pode montar um projeto? O que fazer em tal situação? Aguarde o autor perceber e corrigir? Correto para ele? Reverter alterações? Obviamente, idealmente, vale a pena comprometer apenas um bom código e escrever imediatamente sem erros. Então, esse problema não surgirá.
(para aqueles que não entenderam as dicas do tanque, a conversa é que o efeito negativo se isso acontecer no repositório com 10 desenvolvedores e no repositório com 300 será um pouco diferente)
7) Mesclar bot
Já ouviu falar de uma coisa dessas? Você sabe por que você precisa disso? Você vai rir, mas essa é outra ferramenta que não deveria existir :) Imagine que o tempo de criação do seu projeto é de 30 minutos. E 100 desenvolvedores estão trabalhando no seu projeto. Suponha que cada um deles envie 1 confirmação por dia. Agora imagine um IC honesto, que permite mesclar as alterações no mestre somente depois que elas foram aplicadas à confirmação mais recente do mestre (rebase).
Atenção, a pergunta é: quantas horas devem levar um dia para um servidor de IC tão honesto estrangular as mudanças de todos os desenvolvedores? A resposta correta é 50. Quem respondeu corretamente pode tirar uma cenoura de uma prateleira. Bem, ou imagine como você acabou de cortar seu commit até o último commit no mestre, iniciou a montagem e, quando foi concluído, o master já tinha 20 commits à frente. Tudo de novo?
Portanto, mesclar bot ou fila de mesclagem é um serviço que automatiza o processo de rebasear todas as solicitações de mesclagem para um novo mestre, executando testes e a mesclagem em si, e também pode combinar confirmações em lotes e testá-las juntas. Coisa muito útil. Veja
mergify.io ,
k8s test-infra Prow do Google,
bors-ng , etc. (prometo escrever mais sobre isso no futuro)
Agora, para problemas menos técnicos:
8) Usando uma única ferramenta de construção
Honestamente, ainda é um mistério para mim por que montar todo o repositório mono usando um sistema de compilação comum. Por que não criar javascript com Yarn, java com gradle, Scala com sbt, etc.? Se alguém souber a resposta para esta pergunta (não adivinha ou sugere, ou seja, sabe), escreva nos comentários.
Obviamente, parece óbvio que usar um sistema de compilação é melhor do que vários diferentes. Mas eles ainda entendem que qualquer coisa universal é obviamente pior do que uma especializada, porque provavelmente tem apenas um subconjunto de funções de todos os especialistas. Pior ainda, linguagens de programação diferentes podem ter paradigmas diferentes em termos de montagem, gerenciamento de dependências etc., o que será muito difícil de envolver em um wrapper comum. Não quero entrar em detalhes, darei um exemplo sobre o bazel (veja os detalhes em um artigo separado) - encontramos 5 implementações independentes das regras de montagem de javascript para o bazel de 5 empresas diferentes no GitHub, junto com o oficial do Google. Vale a pena considerar.
9) Abordagens gerais
Em resposta ao artigo original, o CTO do Chef escreveu sua resposta
Monorepo: por favor, faça! . Em sua resposta, ele argumenta que "o principal no monorepo é que isso faz você falar e torna as falhas visíveis". Ele quer dizer que quando você quiser alterar sua API, precisará encontrar todos os seus usos e discutir suas alterações com os mantenedores desses trechos de código.
Então, minha experiência é exatamente o oposto. É claro que isso depende muito da cultura de engenharia da equipe, mas vejo desvantagens sólidas nessa abordagem. Imagine que você está usando uma certa abordagem que o serviu fielmente por algum tempo. Por isso, você decidiu, por algum motivo, resolver um problema semelhante, usar um método ligeiramente diferente, possivelmente mais moderno. Qual é a probabilidade de adicionar uma nova abordagem passar por uma revisão?
No meu passado recente, recebi comentários várias vezes como “já temos um caminho comprovado, use-o” e “se você deseja implementar uma nova abordagem, atualize o código em todos os 120 locais onde a abordagem antiga é usada e obtenha a atualização de todas as equipes responsáveis por esses pedaços de código ". Normalmente, o entusiasmo do "inovador" termina aqui.
E quanto, na sua opinião, custará escrever um novo serviço em uma nova linguagem de programação? No repositório - de jeito nenhum. Você cria um novo repositório, grava e até pega o sistema de compilação mais adequado. E agora a mesma coisa no monorepositório?
Entendo perfeitamente que “padronização, reutilização, compartilhamento de código”, mas o projeto deve ser desenvolvido. Na minha opinião subjetiva, um monorepositório impede isso.
10) Código aberto
Recentemente me perguntaram: “
existem ferramentas de código aberto para mono-repositórios? ” Eu respondi: “O problema é que as ferramentas para mono-repositórios, curiosamente, são desenvolvidas dentro do próprio mono-repositório. Portanto, colocá-los em código aberto é bastante difícil! ”
Por exemplo, veja um projeto no Github com um
plugin bazel para o Intellij IDEA . O Google o desenvolve em seu repositório interno e, em seguida, "expele" partes dele no Github com uma perda do histórico de confirmação, sem a capacidade de enviar uma solicitação de recebimento e assim por diante. Eu não acho que seja de código aberto (aqui está um exemplo do
meu pequeno PR , que foi fechado, em vez de uma mesclagem, e as alterações apareceram na próxima versão). A propósito, esse fato foi mencionado no artigo original que mono-repositórios os impedem de postar em código aberto e criar uma comunidade em torno do projeto. Eu acho que muitos não deram muita importância a esse argumento.
Alternativas
Bem, se falarmos sobre o que fazer para evitar todos esses problemas? Há exatamente um conselho: tente ter um repositório o menor possível.
Mas o que o monorepositório tem a ver com isso? E mesmo que essa abordagem prive você da oportunidade de ter repositórios pequenos, leves e independentes.
Quais são as desvantagens da abordagem de poli-repositório? Vejo exatamente 1: a incapacidade de acompanhar quem é o consumidor da sua API. Isso é especialmente verdade na abordagem dos microsserviços que
não compartilham nada , na qual o código não se atrapalha entre os microsserviços. (A propósito, você acha que alguém usa essa abordagem em mono-repositórios?) Infelizmente, esse problema precisa ser resolvido por meios organizacionais ou tente usar ferramentas de navegação de código que suportem repositórios independentes (por exemplo,
https://sourcegraph.com / ).
Que tal comentários como
"tentamos poli-repositórios, mas tivemos que implementar constantemente recursos em vários repositórios de uma só vez, o que foi cansativo e fundimos tudo em uma única caldeira" ? A resposta para isso é muito simples:
"não confunda os problemas da abordagem com decomposição inadequada" . Ninguém afirma que o repositório deve conter exatamente um microsserviço e é isso. Quando eu estava usando poli-repositórios, reunimos perfeitamente uma família de microsserviços intimamente relacionados em um repositório. No entanto, levando em conta que havia mais de 100 serviços, havia mais de 20 desses repositórios.O mais importante a se pensar em termos de decomposição é como esses serviços serão implantados.
Mas e o argumento sobre a versão? Afinal, os mono-repositórios permitem que você não tenha versões e implante tudo, desde um commit! Em primeiro lugar, o controle de versão é o mais simples de todos os problemas mencionados aqui. Mesmo em coisas antigas como o maven, existe um plugin da maven-version que permite fazer o downgrade da versão com apenas um clique. Em segundo lugar, e mais importante, sua empresa possui aplicativos móveis? Nesse caso, você já tem versões e não conseguirá nada disso!
Bem, ainda existe o argumento principal no suporte a repositórios mono - ele permite refatorar toda a base de código em um commit! De fato, não. Conforme mencionado no artigo original, devido às limitações que a implantação impõe. Você deve sempre ter em mente que, por um longo tempo (a duração depende de como seu processo é construído), você terá 2 versões do mesmo serviço em paralelo. Por exemplo, no meu último projeto, nosso sistema ficou nesse estado por várias horas em cada implantação. Isso leva ao fato de que é impossível realizar refatorações globais que afetam as interfaces de interação em um único commit, mesmo em um repositório único.
Em vez de uma conclusão:
Então, aqueles respeitados e poucos colegas que trabalham no Google, Facebook, etc. e venha aqui para defender seus mono-repositórios, quero dizer: "Não se preocupe, você está fazendo tudo certo, aproveite o seu ajuste, que foi gasto centenas de milhares ou milhões de horas humanas. Eles já foram gastos, portanto, se você não usar, ninguém o utilizará. "
E para todos os outros:
"Você não é o Google, não use mono-repositórios!"P.S. como observado pelo respeitado Bobuk no podcast
radio-T ao discutir o artigo original: “Existem ~ 20 empresas no mundo que podem usar um único repositório.
O resto nem deveria tentar .