Escolhendo a estratégia de ramificação certa para lançamentos

11

Começando com uma nova equipe de desenvolvimento em um novo projeto, precisamos definir nossa estratégia de ramificação para o repositório de origem ( por exemplo, Microsoft Team Foundation Server 2010 ). Entramos em uma discussão complicada sobre se devemos ou não ...

Um . Tenha uma ramificação Release a partir da qual desenvolvemos a produção e, em seguida, Label quando algo for realmente liberado

OU

B . Ter uma nova ramificação da versão para cada nova versão em produção (por exemplo, versões 1, 2, 3, etc ... )

A opção A parece bastante direta, mas não temos certeza se isso levará a problemas a longo prazo. A opção B parece que apenas cria muitos ramos de uma só vez que se acumulam com o tempo.

Alguém tem alguma experiência que possa nos ajudar a decidir? Especificamente, estou procurando ouvir onde estão os pontos problemáticos para qualquer escolha. Sinta-se à vontade para fornecer uma experiência específica em relação às implicações do TFS e / ou do Gerenciamento de Liberação.

JoeGeeky
fonte

Respostas:

15

Opção A. Apenas usando a linha principal e a marcação para liberação

Prós:

  • Você evita o inferno.
  • Manter a linha principal incentiva algumas práticas recomendadas, como o planejamento adequado de lançamento, não a introdução de muito WIP, o uso da ramificação por abstração para lidar com trabalhos de longo prazo fora da banda e o uso do sistema aberto aberto e dos recursos configuráveis ​​para lidar com o gerenciamento de obras em andamento que pode; ou não pode; precisa ser desativado agora ou no futuro para liberar ou evitar uma reversão completa.

Contras:

  • Lidar com trabalhos em andamento se torna um problema e aumenta a área potencial de ataque à superfície quando chega a hora de liberar. No entanto, se seus desenvolvedores são disciplinados, os novos recursos devem ser configuráveis ​​e modulares e, portanto, facilmente desativados / ativados, ou não há WIP e em cada ponto de lançamento todo o trabalho é concluído ou ainda não foi iniciado (por exemplo, Scrum).
  • Mudanças em larga escala / fora de banda requerem mais reflexão antecipadamente para serem implementadas (por exemplo, ramificação por abstração).

Pessoalmente, prefiro essa abordagem. A cobertura do código e os testes de unidade devem identificar o código que não está pronto para sair pela porta e as pessoas não devem estar trabalhando no código que não será liberado durante a iteração atual. Ramificações por abstração ou outros mecanismos podem ser usados ​​para lidar com mudanças de longo prazo e trabalhos em andamento.

Quando você não faz isso, começa a lidar com problemas de mesclagem, código obsoleto, recursos que nunca são lançados etc.

Opção B. Ramificar por liberação

Prós:

  • Você pode começar a trabalhar na próxima iteração enquanto a iteração atual termina sua rodada de testes de aceitação.
  • Outras coisas tenho certeza.

Contras:

  • Toneladas de galhos.
  • Ainda é necessário marcar as ramificações nos pontos de liberação.
  • Ainda é necessário lidar com o WIP e mesclar o WIP do ramo de lançamento anterior para o próximo ramo de lançamento, se não for necessário e ainda precisar desativá-lo ou arrancá-lo do ramo de lançamento e executar novamente os testes de aceitação.
  • As correções precisam ser aplicadas a mais ramificações (release branch + hotfix + nova tag + mesclar hotfix na ramificação vnext e possivelmente vnextnext dependendo de onde o hotfix se encaixa.)

Eu não sou um grande fã desta solução ^ _ ^.

Geralmente eu recomendaria apenas tentar manter a linha principal. Se seus desenvolvedores estão tendo problemas para não escrever o WIP que pode ser facilmente arrancado quando falha no corte ou é verificado antes para a próxima versão, você pode começar a falar sobre a marcação do código no ponto em que ele deve estar completo e ramificado a partir daí, se necessário, para solucionar defeitos e bugs negligenciados que os testes de unidade dos desenvolvedores não conseguiram detectar.

Idealmente, acho que você deseja que esse seja o processo de exceção, não a regra.

Opção C. Opção de bônus louco

Se você quiser se divertir, também pode considerar um modelo de ramificação por história de usuário / por recurso. ( Uma péssima idéia no TFS ou em qualquer DVCS que não seja ao mesmo tempo incrivelmente trivial de implementar se estiver usando um DVCS como git ou mercurial ).

No passado, implementei o abaixo para uma equipe de manutenção de empregadores anteriores que trabalhava com uma base de código legada que não podia ser facilmente transportada para o mercurial a partir do svn. Muito trabalho desnecessário foi envolvido para atender a um requisito comercial de uma linha principal sempre liberável, em vez de apenas coordenar melhor as versões. . .

  1. Os recursos foram desenvolvidos por desenvolvedores em suas equipes.
  2. Quando um recurso está pronto para ser revisado por pares, os desenvolvedores o agrupam em uma única mesclagem do ramo Dev no ramo CR e incluem o ID do recurso / história do usuário no título. * Imposto pelo gancho de pré-confirmação *
  3. Depois de passar pelo CR, uma ferramenta administrativa é usada para promover o recurso na ramificação do controle de qualidade. (Escrevi um pequeno aplicativo de terminal que listava as histórias de usuários presentes nos vários estágios de aceitação e permitia ao operador promovê-lo ou rebaixá-lo entre esses estágios de aceitação)
  4. O controle de qualidade executa testes de automação e usabilidade manual. Se o recurso for bom, ele será enviado para o ramo de lançamento (linha principal). Se o recurso for rejeitado, ele será rebaixado / revertido para fora da ramificação do controle de qualidade até que os desenvolvedores possam resolver os problemas levantados durante o teste e adicionar um patch à ramificação CR.
  5. Se o código foi revertido a partir da ramificação do controle de qualidade e uma correção for aplicada, a ferramenta terminal reaplicará as alterações necessárias para trazer o recurso de volta à ramificação do controle de qualidade a partir da ramificação CR, para que o controle de qualidade possa revisar novamente o código e promovê-lo ou rebaixe-o novamente.
  6. A qualquer momento, o ramo de liberação deve estar em um estado liberável estável.
  7. Após o lançamento, novos Dev, QA e CR são gerados a partir da linha principal.
Keith Brings
fonte
@Keith_Brings Este é um resumo muito bom, obrigado. Como você já indicou, a opção C não é realmente uma opção, pois estou usando o TFS, mas interessante, no entanto.
JoeGeeky
Não vejo como a opção A pode funcionar. Na minha empresa, temos lançamentos diferentes para clientes diferentes. Se ainda estamos desenvolvendo o recurso / hotfix na versão 1.0 e também trabalhando ativamente na versão 2.0 e talvez até na 3.0, também não podemos fazer tudo isso em um único ramo. Talvez você tenha o luxo de aproveitar a opção A por causa do seu modelo de lançamento. Mas isso não é modelo de liberação de todos, e para aqueles de nós preso com fluência recurso ou vários lançamentos paralelos, temos que usar Opção B.
void.pointer
6

Temos ramificações separadas para cada versão lançada (aproximadamente 4 por ano). É muito conveniente quando você precisa obter uma versão específica.

Se você precisar manter algumas versões mais antigas, não acho que essa rotulagem funcionaria. Com ramificações de release específicas, é possível aplicar hot-fixs a cada ramificação separadamente (ou a uma seleção delas) sem se preocupar com nenhum dos outros releases.

Isso também facilita a comparação de versões quando você procura quando um bug ou um recurso foi introduzido.

Não se preocupe com o número de filiais ou com o tempo que elas passam sem alterações. Seu sistema de controle de versão é para fornecer controle e fornecer um histórico do desenvolvimento do seu projeto. A história tem uma tendência a não mudar ... E não se preocupe com o fato de o seu CV não ser capaz de lidar. Usamos o Perforce, mais de 9000 arquivos em uma ramificação de desenvolvimento, até 50 ramificações de desenvolvimento para as versões em que estamos trabalhando e, como já foi dito, uma única ramificação por versão que publicamos. Perforce não está nem respirando mais.

Em resumo: facilite sua vida como desenvolvedor / mantenedor / solucionador de problemas / caçador de problemas e não se preocupe com o número de ramificações ou o número de arquivos. Qualquer CV que se preze irá lidar.

Editar:

Não sofremos nenhuma confusão com relação ao número de filiais que temos. Nosso esquema de nomenclatura para as ramificações de liberação e nossa política de ramificação 1 questão 1 para as ramificações de desenvolvimento (ou trabalho) podem ter algo a ver com isso.

As ramificações de liberação são nomeadas para a liberação que possuem, ou seja: R2011SP1 para a Release 2011 Service Pack 1. Nossas ramificações de trabalho têm nomes menos inteligentes: sub01, sub02, sub03 etc. A "sub" vem do fato de que todas as ramificações de trabalho são sub ramificações do ramo de aceitação. O ramo de aceitação é aquele em que todos os problemas são coletados e estão prontos para serem liberados.

Nossa política de ramificação de trabalho 1 questão 1, combinada com o fato de nosso sistema de rastreamento de problemas ter sido personalizado com um campo "ramificação" garante que sempre saibamos qual problema foi desenvolvido em qual ramificação. Quando um problema é integrado à ramificação de aceitação, este campo é atualizado. Isso significa que sempre sabemos quais problemas estão prontos para lançamento (uma vez que o teste de aceitação é concluído). Da mesma forma, atualizamos esse campo quando um ramo de lançamento é criado e, dessa forma, sempre podemos rastrear em qual lançamento um problema foi lançado.

Marjan Venema
fonte
1
Eu acredito que você pode ramificar de etiquetas no TFS. Portanto, você deve concordar com as correções nas versões atuais do produto, desde que não tenha esquecido o rótulo.
Keith traz
@KeithBrings Está correto, eu apenas testei isso e você pode realmente ramificar de um rótulo.
JoeGeeky
@MarjanVenema Não estou tão preocupado com a carga no sistema quanto com a confusão que um grande número de ramificações pode causar. Também estou um pouco preocupado com o fato de que as alterações feitas na pilha de ramificações de lançamento não serão mescladas com outras ramificações de lançamento que deveriam obtê-las, independentemente da linha principal. Você já se deparou com esse tipo de problema?
JoeGeeky
@ JoeGeeky: não, nenhuma confusão. Veja a atualização para minha resposta.
Marjan Venema
2

É tudo sobre contexto: com que frequência você libera e o que está em uma liberação.

Aqui está um pouco de um estudo de caso que eu tive com meu trabalho antigo, usando o método B (chamamos de ramificação por objetivo ).

Para colocar a história em contexto,

  • Um lançamento consistia em novos recursos em nosso software: novos modos de jogo, novas funcionalidades, novas opções de configuração.
  • O ciclo de lançamento foi bastante longo: nossos clientes eram universidades que mantinham um conjunto de recursos por um ano.

O desenvolvimento principal foi feito no tronco até atingirmos um estado completo do recurso para uma determinada versão. Nesse ponto, criaríamos uma ramificação, digamos projectname-january2012 e faríamos nossos testes de qualidade e correções de bugs nessa mesma ramificação. Quando estávamos prontos para um lançamento público, marcávamos o código nesse ramo e lançávamos.

No entanto, o desenvolvimento do lançamento não terminou nessa tag. Inevitavelmente, tivemos clientes que encontraram bugs ou pequenos problemas com o lançamento. Portanto, nesse caso, tudo o que precisamos fazer é voltar para esse ramo, corrigir o código e criar uma nova versão marcada do ramo janeiro de 2012 a ser lançada e mesclar as correções de volta ao tronco.

No nosso caso, essa abordagem foi favorável porque alguns usuários preferiram ficar com versões mais antigas com um conjunto limitado de recursos ou simplesmente porque o custo de implantar na infraestrutura uma versão totalmente nova em vez de um hotfix causou alguns problemas.

Portanto, as perguntas que você deve se fazer são:

  • Com que frequência libero?
  • Meus lançamentos serão 100% compatíveis com versões anteriores?
  • Meus clientes ficarão bem com a atualização completa para corrigir bugs?

Se você liberar com frequência, talvez não valha a pena ter ramificações para cada um deles. No entanto, se o seu ciclo de lançamento for bastante longo, como o meu caso de uso antigo, e que a implantação, a compatibilidade com versões anteriores e os clientes que se apegam a lançamentos antigos podem ser riscos, a opção B certamente poupará muita dor, facilitará muito o suporte às coisas seus clientes a um custo mínimo para lidar com a confusão de filiais.

Bushibytes
fonte
Eu gosto de como você chama essa opção. Nesse caso, somos nossos próprios clientes ( de certa forma ), portanto a implantação permanecerá amplamente sob nosso controle. Também somos uma loja Scrum e esperamos ter ciclos de lançamento bastante frequentes ( por exemplo, a cada 2-4 semanas ). Embora esperemos oferecer suporte a atualizações contínuas, a compatibilidade com versões anteriores será um problema apenas pelo tempo necessário para implementá-las, portanto ... minutos, talvez. A partir desse som; na sua experiência; a opção B pode não ser a melhor escolha para mim. Obrigado pela informação, muito interessante.
JoeGeeky
Sim, nesse caso, a opção B parece desordem com pouco retorno. Eu só queria destacar que as duas opções são viáveis ​​e têm suas vantagens. Esqueci de mencionar explicitamente: como você lida com correções de bugs? Eles são colocados exclusivamente em novos lançamentos ou em patches / lançamentos antigos?
Bushibytes
1

Eu prefiro a opção A. Desenvolver as liberações de tronco e ramificação quando estão estáveis. Isso limita significativamente o trabalho de integração de hot fixes aplicados ao release de produção.

Fui contratado para ajudar uma equipe que tentou a opção B a voltar aos trilhos.

Algumas coisas a considerar.

  • Migrar hotfixes para frente através de todas as ramificações de código ativas. Isso pode ser feito mesclando, corrigindo e / ou redesenvolvendo. Eles devem ser totalmente gerenciados para garantir que uma correção seja aplicada a todas as versões apropriadas e depois ao tronco.
  • Considere ramificações de recursos para permitir o desenvolvimento de recursos isoladamente do fluxo de código principal. Estes são recomendados para alterações experimentais. Sinta-se livre para abandonar ramificações de recursos, se o recurso não funcionar.
  • Identifique e acompanhe seus pontos de mesclagem.
  • Ramifique seus releases quando necessário. Acho que isso normalmente ocorre quando o release está pronto para a criação do candidato ao release. Em alguns casos, a introdução de alterações incompatíveis no tronco pode forçar e ramificar antecipadamente. Considere um ramo de recurso.
BillThor
fonte
0

Eu trabalhei por alguns anos em um sistema que usa algo entre os dois esquemas que você descreve. A chave é que existe um esquema de numeração multinível em uso. O nível externo é basicamente a versão da API, que é gerenciada em ramificações (com mesclagens cruzadas apropriadas quando algo precisa ser corrigido em várias ramificações) e o nível interno são as liberações exatas feitas, que são gerenciadas com tags.

Em particular, se sabemos qual a versão exata de um cliente, sabemos exatamente de que origem o código foi criado e podemos criar uma duplicata exata para que possamos ver exatamente o que está acontecendo. Isso é muito importante para o suporte! No entanto, o nível externo das ramificações, as versões da API que lançamos atualmente, evoluem com o tempo (com o tronco principal do desenvolvimento obtendo a maioria dos novos recursos). Além disso, quando fazemos uma nova versão principal da API, fazemos uma nova ramificação para suportá-la (para que o tronco sempre possa ser orientado para o desenvolvimento do núcleo duro) e consideramos se devemos encerrar a vida útil do suporte mais antigo atualmente ramo.

Portanto, recomendo algo que seja realmente uma mistura de A e B ; ambos têm bons aspectos, mas nenhum deles é completo em si. Use o melhor dos dois mundos.

Donal Fellows
fonte
0

Eu usei o TFS para implementar efetivamente a opção (B) no passado.

A ramificação / mesclagem é uma ótima ferramenta quando é feita em pedaços pequenos. A dificuldade não está em criar um ramo (isso é estúpido e fácil), nem em empurrar o trabalho de uma semana para cima da árvore (que geralmente também é fácil) ... é fazer com que o sistema de IC por trás do controle de origem trabalhe automaticamente para você.

Porque a ramificação é discutível se o sistema não estiver construindo e executando testes automaticamente para sua ramificação.

Personalizamos o fluxo de trabalho de construção padrão do TFS para reconhecer os caminhos relativos dos conjuntos de alterações e estabelecemos uma convenção pela qual a personalização poderia reconhecer uma nova ramificação (em vez de simplesmente uma nova subpasta sob alguma raiz de desenvolvimento). Era suave, fácil de ramificar, fácil de matar um galho, e recebemos feedback contínuo do nosso sistema para compilações e testes.

Eu vejo muitas pessoas declarando como essas estratégias são impossíveis no TFS e acredito que isso se deve à falta de familiaridade com as possibilidades de um mecanismo de construção baseado em XAML. O TFS não é apenas controle de origem, é uma solução completa e deve ser usada como tal.

Craig Brunetti
fonte