Melhor estratégia de ramificação ao fazer integração contínua?

100

Qual é a melhor estratégia de ramificação a ser usada quando você deseja fazer integração contínua?

  1. Ramificação de versão: desenvolva no tronco, mantenha uma ramificação para cada versão.
  2. Ramificação de recurso: desenvolva cada recurso em uma ramificação separada, mescle apenas quando estiver estável.

Faz sentido usar essas duas estratégias juntas? Tipo, você ramifica para cada versão, mas também ramifica para grandes recursos? Uma dessas estratégias combina melhor com a integração contínua? Usar a integração contínua faria sentido ao usar um tronco instável?

KingNestor
fonte
2
Nota lateral: alguns argumentariam que mesmo quando novos recursos são introduzidos, tudo deve estar sempre estável. Por outro lado, isso pode ser um tanto idealista.
Keith Pinson

Respostas:

21

Acho o assunto muito interessante, pois confio muito em ramos no meu trabalho diário.

  • Lembro-me de Mark Shuttleworth propondo um modelo sobre como manter o branch principal intocado enquanto vai além do CI convencional. Eu postei sobre isso aqui .
  • Como estou familiarizado com o Cruise Control, também escrevi sobre ramificações de tarefas e CI aqui . É um tutorial passo a passo explicando como fazer isso com o Plastic SCM .
  • Finalmente, achei alguns dos tópicos sobre CI (e potencialmente falando sobre ramificação) no livro de Duvall sobre CI muito interessantes também .

Espero que você ache os links interessantes.

pablo
fonte
Adicionamos suporte ao Bamboo para fazer branch por tarefa codicesoftware.blogspot.com/2012/02/… , e parece que sua versão mais recente fará isso nativamente com vários controles de versão, incluindo dvcs.
pablo,
20

A resposta depende do tamanho de sua equipe e da qualidade de seu controle de origem e da capacidade de mesclar conjuntos de mudanças complexos corretamente. Por exemplo, em controle de fonte de filial completo como CVS ou mesclagem de SVN pode ser difícil e você pode ficar melhor com o primeiro modelo, enquanto se usar um sistema mais complexo como IBM ClearCase e com um tamanho de equipe maior, você poderia ser melhor com o segundo modelo ou uma combinação dos dois.

Eu, pessoalmente, separaria o modelo de branch de recurso, onde cada recurso principal é desenvolvido em um branch separado, com sub-branches de tarefa para cada mudança feita por desenvolvedor individual. Conforme os recursos se estabilizam, eles são mesclados ao tronco, que você mantém razoavelmente estável e passa em todos os testes de regressão o tempo todo. Conforme você se aproxima do fim do seu ciclo de lançamento e todos os ramos de recursos se fundem, você estabiliza e ramifica um ramo do sistema de lançamento no qual você apenas faz correções de bugs de estabilidade e backports necessários, enquanto o tronco é usado para o desenvolvimento da próxima versão e você novamente ramificar para novos ramos de recursos. E assim por diante.

Desta forma, o trunk contém sempre o código mais recente, mas você consegue mantê-lo razoavelmente estável, criando rótulos estáveis ​​(tags) nas principais mudanças e fusões de recursos, os branches de recursos são de desenvolvimento rápido com integração contínua e sub-branches de tarefas individuais podem ser frequentemente atualizado a partir do branch de recursos para manter todos trabalhando no mesmo recurso em sincronia, enquanto simultaneamente não afeta outras equipes que trabalham em recursos diferentes.

Ao mesmo tempo, você tem ao longo da história um conjunto de ramos de lançamento, onde você pode fornecer backports, suporte e correções de bugs para seus clientes que por qualquer motivo permanecem nas versões anteriores do seu produto ou mesmo apenas na última versão lançada. Assim como com o tronco, você não configura a integração contínua nos ramos de lançamento, eles são cuidadosamente integrados ao passar em todos os testes de regressão e outros controles de qualidade de lançamento.

Se por algum motivo dois recursos são co-dependentes e precisam de mudanças feitas um pelo outro, você pode considerar desenvolver ambos no mesmo ramo de recurso ou exigir que os recursos mesclem regularmente partes estáveis ​​do código para o tronco e, em seguida, atualize as alterações de tronco para trocar código entre ramos de tronco. Ou, se você precisar isolar esses dois recursos de outros, pode criar uma ramificação comum a partir da qual ramificará essas ramificações de recursos e que poderá usar para trocar código entre os recursos.

O modelo acima não faz muito sentido com equipes com menos de 50 desenvolvedores e sistema de controle de origem sem ramificações esparsas e capacidade de mesclagem adequada como CVS ou SVN, o que tornaria todo este modelo um pesadelo para configurar, gerenciar e integrar.

Jiri Klouda
fonte
5
Não tenho certeza se concordo que o que você descreve não faz sentido para equipes com menos de 50 desenvolvedores. Também posso ver benefícios para equipes muito menores. +1
Aardvark
2
Claro, há benefícios para equipes de qualquer tamanho. A questão é em que tamanho da equipe os benefícios superam os custos associados a um processo pesado.
Jiri Klouda de
Isso é semelhante ao modelo GitFlow e / ou GitHubFlow. Não acho que esses modelos facilitem a Integração Contínua (IC). Na minha opinião, o desenvolvimento baseado em tronco é uma melhoria significativa nesses modelos.
Yani de
Você pode ver que este comentário realmente é anterior ao lançamento original do fluxo git. Não tenho certeza do que você entende por "melhor". Eu apoiei equipes de 1, 5, 25, 150, 1.000 e 20.000 desenvolvedores trabalhando em projetos que foram integrados até certo ponto. Os requisitos variam e "melhor" é um termo muito relativo. Você precisa fazer backport do código alguma vez? Correções de segurança? Do contrário, sua vida é simples. SaaS é um resultado direto de restrições impostas pelo desenvolvimento baseado em tronco. Sinalizadores de recursos são tão complexos quanto ramificações de recursos. Exceto que você só descobre com os clientes quando uma permutação deles é interrompida.
Jiri Klouda
9

Eu pessoalmente acho muito mais limpo ter um tronco estável e ramificar recursos. Dessa forma, os testadores e similares conseguem ficar em uma única "versão" e atualizar do tronco para testar qualquer recurso que seja de código completo.

Além disso, se vários desenvolvedores estiverem trabalhando em recursos diferentes, todos eles podem ter seus próprios ramos separados e, em seguida, mesclar para o tronco quando terminar e enviar um recurso para ser testado sem que o testador precise alternar para vários ramos para testar recursos diferentes.

Como um bônus adicional, há algum nível de teste de integração que vem automaticamente.

Adnan
fonte
Além disso, você ainda ramifica e marca para cada versão principal? Ou apenas marcar?
KingNestor
1
Ele funciona bem com CI, desde que os branches do recurso sejam mesclados no tronco com alguma disciplina para não ter compilações quebradas. Eu faço branch e tag para cada versão de produção que será usada apenas para correção de bugs. Isso pode ser incorporado ao tronco estável imediatamente.
Adnan
@king, eu diria que provavelmente depende do que você chama de lançamento principal, mas em ambos os casos você pode marcar e ramificar mais tarde quando precisar (com base na tag :))
eglasius
5

Acho que qualquer uma das estratégias pode ser usada com desenvolvimento contínuo, desde que você se lembre de um dos princípios-chave que cada desenvolvedor se compromete com o tronco / linha principal todos os dias.

http://martinfowler.com/articles/continuousIntegration.html#EveryoneCommitsToTheMainlineEveryDay

EDITAR

Tenho feito algumas leituras deste livro sobre CI e os autores sugerem que a ramificação por lançamento é sua estratégia de ramificação preferida. Eu tenho que concordar. Ramificar por recurso não faz sentido para mim ao usar CI.

Vou tentar explicar porque estou pensando dessa maneira. Digamos que três desenvolvedores cada um pegue um ramo para trabalhar em um recurso. Cada recurso levará vários dias ou semanas para ser concluído. Para garantir que a equipe esteja continuamente integrada, eles devem se comprometer com a filial principal pelo menos uma vez por dia. Assim que começam a fazer isso, perdem o benefício de criar um branch de recurso. Suas alterações não são mais separadas de todas as alterações do outro desenvolvedor. Sendo esse o caso, por que se preocupar em criar ramificações de recursos em primeiro lugar?

Usar ramificação por lançamento requer muito menos fusão entre ramos (sempre uma coisa boa), garante que todas as mudanças sejam integradas o mais rápido possível e (se feito corretamente) garante que sua base de código esteja sempre pronta para lançamento. O lado ruim da ramificação por lançamento é que você precisa ser consideravelmente mais cuidadoso com as mudanças. Por exemplo, a refatoração grande deve ser feita de forma incremental e se você já integrou um novo recurso que não deseja na próxima versão, então ele deve ser oculto usando algum tipo de mecanismo de alternância de recursos .

OUTRA EDIÇÃO

Existe mais de uma opinião sobre este assunto. Aqui está uma postagem de blog que é um recurso profissional ramificado com CI

http://jamesmckay.net/2011/07/why-does-martin-fowler-not-understand-feature-branches/

Phil Hale
fonte
interessante, não consigo mais encontrar este post.
Jirong Hu
5

Ramificações de lançamento são muito úteis, e até mesmo absolutamente necessárias, se você precisar manter várias versões de seu aplicativo.

Ramificações de recursos também são muito convenientes, principalmente se um desenvolvedor precisar trabalhar em uma grande mudança, enquanto outros ainda lançam novas versões.

Portanto, para mim, usar os dois mecanismos é uma estratégia muito boa.

Link interessante do Livro de SVN .

SirFabel
fonte
4

Recentemente, passei a gostar desse modelo ao usar o git. Embora sua pergunta esteja marcada como "svn", você ainda poderá fazer algum uso dela.

A integração contínua pode, até certo ponto, acontecer no branch "desenvolver" (ou como você quiser) neste modelo, embora ter branches de recursos de longa execução para lançamentos futuros não o tornaria tão rígido a ponto de considerar cada mudança acontecendo no código em algum lugar. A questão permanece, se você realmente deseja isso. Martin Fowler, sim.

Hermannloose
fonte
2

A integração contínua não deve ser nenhum tipo de fator na determinação de sua estratégia de ramificação. Sua abordagem de ramificação deve ser selecionada com base em sua equipe, o sistema em desenvolvimento e as ferramentas disponíveis para você.

Tendo dito isto ...

  • não há razão para que o CI não possa ser usado em ambas as abordagens que você descreve
  • essas abordagens funcionam muito bem em combinação
  • nenhum dos dois funciona "melhor" do que o outro
  • CI faz todo o sentido com um tronco instável

Tudo isso foi respondido na quarta pergunta da página de onde você tirou os diagramas: http://blogs.collab.net/subversion/2007/11/branching-strat/

Zac Thompson
fonte
2

Contanto que você entenda os princípios, você sempre pode reinventar as melhores práticas. Se você não entender os princípios, as melhores práticas o levarão muito longe antes de desmoronar devido a algum requisito externo conflitante.

Para a melhor introdução ao Mainline Model, leia isto: https://web.archive.org/web/20120304070315/http://oreilly.com/catalog/practicalperforce/chapter/ch07.pdf

Leia o link. Depois de aprender o básico, leia o seguinte artigo do venerável Henrik Kniberg. Isso o ajudará a relacionar o Mainline Model com integração contínua.

http://www.infoq.com/articles/agile-version-control

zvolkov
fonte
Capítulo O'Reilly não está mais acessível
Jason S,
1

Quando começamos nossa equipe, herdamos uma estratégia baseada no lançamento do fornecedor que originalmente desenvolveu o sistema que estávamos prestes a assumir. Funcionou até o momento em que nossos clientes solicitaram que vários recursos desenvolvidos não fossem incluídos em uma versão (fyi ~ 250k linhas de código, ~ 2500 arquivos, Scrum com XP SDLC).

Então começamos a olhar para branches baseados em recursos. Isso também funcionou por um tempo - como 2 meses até o ponto em que percebemos que nosso processo de teste de regressão levaria mais de 2 semanas, o que combinado com a incerteza do que seria lançado criou um grande inconveniente.

O "prego no caixão" final das estratégias SC puras veio quando decidimos que deveríamos ter 1. tronco estável e 2. A produção deve conter ST, UAT e BINÁRIOS testados por regressão (não apenas na origem - pense em CC).

Isso nos levou a criar uma estratégia que é um híbrido entre estratégias de SC baseadas em recursos e versões.

Portanto, temos um baú. A cada sprint, ramificamos o branch sprint (para o pessoal não ágil - um sprint é apenas um esforço de desenvolvimento com tempo limitado e saída variável com base na complexidade). No branch sprint, criamos os branches de recursos e o desenvolvimento paralelo começa neles. Depois que os recursos são concluídos e o sistema testado, e recebemos a intenção de implantá-los, eles são mesclados ao branch do sprint - alguns podem flutuar em vários sprints, geralmente os mais complexos. Uma vez que o sprint está próximo do fim e os recursos estão completos ... nós "renomeamos" o branch do sprint para "regressão" (isso permite que o CruiseControl o pegue sem qualquer reconfiguração) e, em seguida, os testes de regressão / integração começam no cc-construído ORELHA. Quando tudo estiver pronto, ele vai para a produção.

Em suma, ramificações baseadas em recursos são usadas para desenvolver, testar o sistema e a funcionalidade do UAT. A ramificação sprint (na verdade, a ramificação de lançamento) é usada para mesclar seletivamente recursos sob demanda e teste de integração.

Agora, aqui está uma pergunta para a comunidade - obviamente estamos tendo problemas para realizar a integração contínua devido ao fato de que o desenvolvimento acontece em muitas ramificações e à sobrecarga de reconfiguração do CruiseControl. Alguém pode sugerir e aconselhar?

XAvatar
fonte
Não concordo necessariamente com as conclusões, mas obrigado pela discussão do seu processo. Não existe uma solução única para todos.
RaoulRubin de
0

A meu ver, você deseja ter um conjunto limitado de ramos onde possa se concentrar. Já que você deseja testes, métricas de qualidade de código e muitas coisas interessantes para executar com as compilações, ter muitos relatórios provavelmente fará com que você perca informações.

Quando e o que ramificar, geralmente depende do tamanho da equipe e do tamanho dos recursos que estão sendo desenvolvidos. Não acho que haja uma regra de ouro. Certifique-se de usar uma estratégia em que possa obter feedback com antecedência / frequência, e isso inclui ter qualidade envolvida desde o início dos recursos. A parte da qualidade significa que, conforme você está automatizando à medida que a equipe se desenvolve, se você ramificar para um grande conjunto de recursos que uma equipe está construindo, terá que ter qualidade envolvida na equipe também.

ps Onde você conseguiu essas referências de abordagem? - não sinto que esses gráficos representam todas as opções

Atualização 1: explicando por que eu disse que não é uma regra de ouro. Basicamente, para equipes relativamente pequenas, descobri que é melhor usar uma abordagem mista. Ramificações de recursos são criadas se for algo longo e parte da equipe continuará adicionando recursos menores.

Eglasius
fonte
Tem mais alguns também. Mas eu sinto que Feature Branching e Release Branching são as duas mais comuns.
KingNestor
0

Dave Farley , autor de Continuous Delivery , referiu-se ao Trunk Based Development (TBD) como a pedra angular da Continuous Integration (CI) e Continuous Delivery (CD). Ele diz:

Qualquer forma de ramificação é antitética à integração contínua.

Ele também diz,

Feature Branching é muito bom da perspectiva de um desenvolvedor individual, mas abaixo do ideal da perspectiva de uma equipe. Todos nós gostaríamos de ser capazes de ignorar o que todos os outros estão fazendo e continuar nosso trabalho. Infelizmente, o código não é assim. Mesmo em bases de código muito bem fatoradas com bela separação de interesses e componentes maravilhosamente fracamente acoplados, algumas alterações afetam outras partes do sistema.

Desenvolvimento baseado em tronco (TBD) é a prática de integrar alterações de código no tronco (também conhecido como mestre, linha principal) pelo menos uma vez por dia - de preferência várias vezes por dia. Integração contínua (CI) é uma prática semelhante, exceto que também envolve a verificação das alterações de código usando testes automatizados. A melhor estratégia de ramificação para isso é trabalhar diretamente fora do tronco e realizar revisões de código por meio da Programação em par . Se, por algum motivo, você não conseguir emparelhar ou apenas quiser ramificar, certifique-se de que seus ramos tenham vida curta (menos de um dia).

Eu trabalho no Trunk, “master” nos meus repositórios GIT. Eu me comprometo a dominar localmente e enviar imediatamente, quando estiver em rede, para meu repo master central onde o CI é executado. É isso aí!

Para recursos grandes (ou seja, aqueles que levam mais de um dia), tente dividi-los em pequenos pedaços de lógica que podem ser integrados ao tronco sem quebrar o software. Você também pode usar técnicas como sinalização de recurso e ramificação por abstração, que permitem implantar trabalho incompleto sem afetar os usuários finais.

Eu uso branch by abstraction, dark-release e às vezes feature-flags. O que recebo em troca é um feedback rápido e definitivo (pelo menos para a qualidade dos meus testes).

Yani
fonte
Dave Farley e Jez Humble estão simplesmente errados em sua postura quanto à ramificação. A razão para isso é que ele codifica a suposição importante "você nunca terá que manipular o código no nível do recurso e se, então, é uma operação cara" e eles baseiam sua avaliação em outra suposição "a fusão é muito cara com a automatizada mescla sendo quase impossível em escala ". Se essas duas suposições não forem verdadeiras, se você vive em um mundo onde a fusão é barata, mas precisa manipular o código em nível de recurso para portas traseiras e correções de segurança, então suas declarações não funcionam. É um caso raro.
Jiri Klouda
Algumas empresas também precisam mover recursos para lançamentos futuros, depois que esses recursos enfrentam obstáculos na implementação e estão atrasando um lançamento. Às vezes, há uma opção de deixar o código, como em produtos SaaS, mas se o código for liberado para os clientes, pode não ser uma opção, pois pode ser analisado pelos concorrentes. Tanto código hoje em dia não é compilado e, mesmo que seja, os sinalizadores de define / recursos no código estão no mesmo nível de complexidade que os branches.
Jiri Klouda
-3

Acho que as ferramentas que você usa são um grande fator aqui.

  • Se você estiver usando o subversion, continue com a opção 1 e libere dos branches.
  • Se você estiver usando o GIT, a opção 2 funcionará bem para você.
Tony Zampogna
fonte
2
A ramificação de recursos pode ser facilmente alcançada com qualquer SCM
hdost