Como refatorar quando todo o seu desenvolvimento está nas filiais?

24

Na minha empresa, todo o nosso desenvolvimento (correções de bugs e novos recursos) é realizado em filiais separadas. Quando está completo, enviamos para o controle de qualidade que o testa nesse ramo e, quando eles nos dão luz verde, o mesclamos em nosso ramo principal. Isso pode levar entre um dia e um ano.

Se tentarmos compactar qualquer refatoração em uma ramificação, não sabemos por quanto tempo ela ficará "fora", para que possa causar muitos conflitos quando ela for incorporada novamente.

Por exemplo, digamos que eu queira renomear uma função porque o recurso no qual estou trabalhando está fazendo muito uso dessa função e descobri que seu nome não se encaixa realmente em seu objetivo (novamente, este é apenas um exemplo). Então, procuro todos os usos dessa função e os renomeio para seu novo nome, e tudo funciona perfeitamente, então a envio para o controle de qualidade.

Enquanto isso, novos desenvolvimentos estão acontecendo, e minha função renomeada não existe em nenhum dos ramos que estão sendo extraídos do main. Quando meu problema for mesclado novamente, todos eles serão resolvidos.

Existe alguma maneira de lidar com isso?

Não é como se a gerência aprovasse uma questão apenas para refatorar, portanto ela precisa ser incluída em outros trabalhos. Ele não pode ser desenvolvido diretamente no main, porque todas as alterações precisam passar pelo controle de qualidade e ninguém quer ser o idiota que quebrou o main, para que ele possa fazer um pouco de refatoração não essencial.

mpen
fonte
Qual controle de versão você está usando? Existem diferentes abordagens para o DVCS e um modelo de servidor centralizado. Além disso, de que ramos de desenvolvimento estão sendo retirados? Se um ramo de recurso for aceito, como outros ramos de desenvolvimento captam as alterações?
2
Como um aparte, um diagrama da estrutura de ramificação atual pode ser realmente útil. É bem possível que a raiz do problema com a dificuldade de refatoração seja em parte causada por algumas ... políticas de ramificação não convencionais (consulte programmers.stackexchange.com/questions/210360 para um exemplo). Sugiro também a leitura de vance.com/steve/perforce/Branching_Strategies.html para obter algumas idéias e antecedentes (se eu puder responder a essa pergunta, esse será um ponto de referência importante).
1
O último parágrafo resume tudo - se a Empresa não perceber o valor, não há como um grande refatorador avançar. Você precisa trabalhar com sua equipe de teste para resolver os cronogramas. (Suspeito que seu controle de qualidade seja realmente um teste de resistência (eles colocam uma peruca e um batom e fingem ser algo que não são). Uma equipe de controle de qualidade real estaria dizendo o que refatorar, não
atrapalhando
1
@mattnz: Você está certo. Eles não são uma equipe de controle de qualidade real. Eles são principalmente suporte ao cliente. Acho que muitas das responsabilidades deles devem ser transferidas para a equipe de desenvolvimento, porque eles simplesmente não conseguem lidar com tudo o que jogamos sobre eles, mas isso é um problema de gerenciamento e uma batalha que ainda não venci.
MPEN
3
Você perdeu minha escavação. Teste! = QA. O controle de qualidade supervisiona a qualidade e visa melhorar os resultados dos negócios. O teste tenta provar a ausência de defeitos encontrando-os.
mattnz

Respostas:

12

Existem vários problemas que estão se misturando para tornar a refatoração um desafio neste ambiente. Misturados a isso estão alguns problemas não técnicos ("mas isso é um problema de gerenciamento e uma batalha que ainda não venci").

O primeiro problema é o ramo de longa duração. Essas ramificações têm dificuldade em rastrear alterações fora da visualização do desenvolvedor. Para endereçar isto:

  • Quando o código estiver completo - dê uma olhada novamente (deixe o suporte ao cliente examiná-lo, se quiser), mas junte-o rapidamente ao desenvolvimento, para que outras alterações que dependem dele possam ser detectadas e alterações que sejam conflitantes antes. no processo.
  • Se, por algum motivo, um brach se tornar muito demorado enquanto a refatoração estiver em andamento, tende a ser uma boa prática mesclar-se de estável para o ramo para captar alterações e refatoração. Geralmente, isso minimiza conflitos e surpresas na mesclagem do ramo de recursos para o ramo estável.
  • Todos os testes de integração precisam ser feitos em versões - não em recursos . Nesse ambiente, os recursos podem ou não estar totalmente integrados ao sistema. Embora seja possível fazer uma verificação de integridade no recurso isoladamente, ele não identifica problemas no lançamento.
  • Desde o momento da conclusão do código até a fusão (vamos chamá-lo de desenvolvimento - a ramificação do master / stable / release tem seus próprios problemas de não captar as alterações mais recentes do desenvolvimento) não deve demorar muito. Quanto mais você esperar, mais conhecimento será perdido e mais difícil será a integração do código com outras linhas de código.

Outra questão que está se misturando a isso é que eu aludi aos pontos acima é a mudança do papel do ramo ao longo do tempo. Começa como um ramo de desenvolvimento em que os desenvolvedores se comprometem e se torna uma área de teste (que teste está sendo feito aqui que pode ser significativo em todo o aplicativo?), Que é então mesclado em estável (e presumivelmente liberado - é isso? testado novamente?).

Com um recurso mais curto, do início ao fim, é mais fácil que a refatoração possa ser captada por outras ramificações.

Incentive os desenvolvedores a obter todo o ambiente. Apenas mudanças na escolha da cereja podem levar a ... digamos, ambientes interessantes para desenvolvedores. Embora a escolha da cereja tenha seus usos, pode ser preocupante que esse seja o modo padrão de puxar alterações para um ramo.

A refatoração é algo que idealmente é feito constantemente, ou se não constantemente, sempre que houver um mínimo de tempo de inatividade. Ramifique, faça uma refatoração simples, execute os testes de unidade para verificar se tudo ainda está funcionando (sua unidade foi testada, certo? Certo? ) E depois volte ao estábulo. Passe as informações para outros desenvolvedores para extrair as alterações que você refatorou em suas próprias ramificações.

É importante que os desenvolvedores possuam a qualidade do código. Enquanto a direção dos recursos vem de fora e as alocações de tempo geralmente não são as nossas, a qualidade do código é algo que é necessário nos orgulhar e dedicar tempo.

Você pode encontrar as seguintes perguntas úteis na busca de alocação de tempo para lidar com dívidas técnicas:

Você também pode procurar ferramentas como o sonar, que podem ajudar a identificar as áreas do código que precisam de mais trabalho para refatoração. O plugin de dívida técnica - é algo que pode ser usado para ajudar a apontar o acúmulo de dívida ao longo do tempo na base de código.

Frequentemente, é necessário salientar que o ROI para lidar com dívidas técnicas é um tempo de resposta mais rápido para recursos e correções de bugs da equipe de desenvolvimento.

Comunidade
fonte
Os testes são essencialmente realizados em três pontos no tempo. Uma vez quando o problema é reivindicado corrigido (para garantir que ele atenda a todos os requisitos e não haja grandes problemas), novamente quando ele é mesclado novamente ao padrão (teste de integração) e novamente quando fazemos uma compilação (integração com todos os escolhidos pela cereja) questões / análise final). Penso que a colheita de cerejas é necessária em nosso ambiente, pois operamos um SaaS com clientes muito específicos. Vou dar uma olhada nesses links, obrigado pelos ponteiros! Edit: Na verdade, há mais uma olhada na produção para garantir que ela funcionou bem.
MPEN
3

Normalmente, estou desenvolvendo uma versão refatorada em "paralelo" com a corrente, ou seja, na mesma base de código, mas não referenciando-a do aplicativo principal. E quando uma nova solução é feita e testada, estou iniciando a refatoração real.

Exemplo 1. Suponha que eu tenha uma coisa, seja ela função, interface, módulo ou qualquer outra coisa. E eu quero refatorar. Estou criando o Thing2 na mesma base de código, é uma versão refatorada do Thing. Quando estiver pronto e testado, refatoro tudo o que faz referência a Thing, para substituí-lo por Thing2. Geralmente, essa etapa leva uma quantidade relativamente pequena de tempo.

Se a refatoração real levar muito tempo para se manter sincronizada sem estragar a equipe, eu pegarei todos os recursos relevantes e os refatorarei em paralelo também.

Exemplo 2. Eu tenho um novo backend de renderização, que é uma versão refatorada do antigo. Mas não é compatível com o frontend de renderização antigo. Portanto, preciso refatorar o frontend. E novamente: na mesma base de código. Quando tudo estiver pronto, estou apenas mudando a classe da instância de front-end; idealmente, será necessário um pequeno commit.

Sim, recursivamente, pode-se concluir que tudo deve ser feito em paralelo. Mas isso geralmente acontece quando há muito acoplamento na base de código ou está mudando muito rápido.

Finalmente, quando o novo código é integrado e funciona bem, os recursos antigos podem ser removidos da base de código e os novos recursos podem ser renomeados para obter nomes antigos.

Geralmente, a idéia é preparar novos recursos em paralelo e passar a usá-los em um pequeno passo.

John Carmack usa essa abordagem (ou pelo menos semelhante), talvez seu blog explique melhor: (link)

Sombras na chuva
fonte
Essa é uma boa abordagem. Estou tentando lembrar o que realmente motivou essa pergunta agora ... não acho que tenha sido muito favorável à paralelização. Ou, se foi, acho que minha preocupação é que essa abordagem cause muita fragmentação na base de código. Temos "velhas maneiras" de fazer as coisas e "novas maneiras" de fazer as coisas, e as coisas antigas estão sendo substituídas em um ritmo glacial, mas estão causando dor de cabeça aos desenvolvedores, porque agora eles precisam conhecer dois (ou mais) sistemas.
MPEN
1

Pode parecer uma dificuldade no lado técnico quando, na verdade, está no lado dos requisitos.

Onde o desenvolvimento é orientado para diferentes requisitos em diferentes ramos é a verdadeira dificuldade. Os gerentes e arquitetos da equipe devem tomar decisões que possam permitir a coexistência das diferentes necessidades comerciais.

O processo ZBB e o Co-Dev "comprometem-se" quando são tomados após a tomada de decisões corretas, com entradas relevantes de todos os desenvolvedores, e devem permitir que você implemente o que você precisa sem ter que pensar - Como vou mesclar meu código.

ZBB significa orçamento baseado em zero . Ao dizer Co-Dev, eu quis dizer poucas pessoas que estão trabalhando em programação paralela.

Yosi Dahari
fonte
2
o que são "ZBB" e "Co-Dev"?
Gnat #
ZBB - en.wikipedia.org/wiki/Zero-based_budgeting . Ao dizer Co-Dev, eu quis dizer poucas pessoas que estão trabalhando em programação paralela.
Yosi Dahari
1

O problema me parece que você está trabalhando demais nas filiais. O custo dos conflitos cresce exponencialmente com o comprimento que todos ficam em uma filial, portanto, com conflitos muito longos, você tem poucas chances de fazer qualquer refatoração.

gnasher729
fonte
0

Seu problema é o modelo de filial que você está usando. Você pode desenvolver uma ramificação e, quando estiver pronta e pronta para o controle de qualidade, a ramificação será mesclada a um 'tronco intermediário', às vezes chamado de Integração ou Teste. Ao desenvolver o próximo recurso, você pode ramificar desse tronco intermediário.

Esse modelo permite desenvolver vários recursos em paralelo em diferentes ramificações, mesclando-os todos na ramificação de integração para enviar ao controle de qualidade e também manter um único tronco de liberações (você mescla o controle de qualidade da base de código recebido ao tronco principal quando eles o certificam )

Você está assumindo que suas alterações entregues ao controle de qualidade serão aprovadas sem grandes modificações - se o código de controle de qualidade voltar com instruções para remover metade das alterações, será necessário reverter, mas se isso não acontecer, fará seu desenvolvimento muito mais suave. Então, você basicamente retira ramificações de novos recursos do que será o código da linha principal (por exemplo, tronco após a fusão do código passado para o controle de qualidade), em vez do que é hoje (por exemplo, tronco atual) e, portanto, não estará mais desenvolvendo a base de código da versão anterior .

gbjbaanb
fonte