Manter centenas de filiais customizadas sobre o branch master

139

Atualmente, temos um branch master para nosso aplicativo PHP em um repositório compartilhado. Temos mais de 500 clientes que são assinantes do nosso software, a maioria dos quais possui alguma personalização para diferentes propósitos, cada um em uma agência separada. A personalização pode ser um nome de campo de texto diferente, um recurso ou módulo totalmente novo ou novas tabelas / colunas no banco de dados.

O desafio que enfrentamos é que, à medida que mantemos essas centenas de filiais customizadas e distribuímos aos clientes, de tempos em tempos fornecemos novos recursos e atualizamos nossa ramificação principal, e gostaríamos de enviar as mudanças do ramo mestre para filiais personalizadas para atualizá-los para a versão mais recente.

Infelizmente, isso geralmente resulta em muitos conflitos no código personalizado e passamos muitas horas percorrendo todos os ramos para solucionar todos os conflitos. Isso é muito ineficiente e descobrimos que os erros não são incomuns na solução desses conflitos.

Estou procurando uma maneira mais eficiente de manter os nossos balancetes de clientes atualizados com a ramificação principal que resultará em menos esforço durante a mesclagem.

    
por Fernando Tan 09.11.2015 / 17:00
fonte

9 respostas

308

Você está abusando completamente dos ramos! Você deve ter a personalização fornecida pela flexibilidade em seu aplicativo, não a flexibilidade em seu controle de versão (que, como você descobriu, não foi planejado / projetado para esse tipo de uso).

Por exemplo, crie rótulos de campo de texto provenientes de um arquivo de texto e não seja codificado em seu aplicativo (é assim que a internacionalização funciona). Se alguns clientes tiverem recursos diferentes, torne seu aplicativo modular , com limites internos estritos governados por APIs estritas e estáveis, para que os recursos possam ser conectados conforme necessário.

A infraestrutura principal e quaisquer recursos compartilhados só precisam ser armazenados, mantidos e testados uma vez .

Você deveria ter feito isso desde o começo. Se você já tiver quinhentas variantes de produtos (!), Consertar isso será um trabalho enorme … mas não mais do que a manutenção contínua.

    
por 09.11.2015 / 17:03
fonte
92

Ter 500 clientes é um bom problema, se você tivesse gasto o tempo adiantado para evitar esse problema com filiais, talvez nunca tivesse conseguido permanecer no mercado por tempo suficiente para obter clientes.

Em primeiro lugar, espero que você cobra de seus clientes o suficiente para cobrir TODOS os custos de manutenção de suas versões personalizadas. Estou assumindo que os clientes esperam obter novas versões sem ter que pagar para que suas personalizações sejam feitas novamente. Eu começaria encontrando todos os arquivos que são os mesmos em 95% dos seus branches. Que 95% é a parte estável do seu aplicativo.

Em seguida, localize todos os arquivos que possuem apenas algumas linhas diferentes entre as ramificações - tente introduzir um sistema de configuração para que essas diferenças possam ser removidas. Portanto, por exemplo, em vez de ter centenas de arquivos com rótulos de campo de texto diferentes, você tem um arquivo de configuração que pode substituir qualquer rótulo de texto. (Isso não precisa ser feito de uma só vez, basta configurar um rótulo de campo de texto na primeira vez que um cliente quiser alterá-lo.)

Em seguida, passe para as questões mais difíceis usando o padrão de estratégia, injeção de dependência, etc.

Considere armazenar o json no banco de dados em vez de adicionar colunas para os próprios campos do cliente. Isso pode funcionar se você não precisar pesquisar esses campos com o SQL.

Toda vez que você verifica um arquivo em uma ramificação, você DEVE diferenciá-lo com main e justificar cada alteração, incluindo o espaço em branco. Muitas mudanças não serão necessárias e podem ser removidas antes do check-in. Isso pode ser apenas um desenvolvedor com diferentes configurações em seu editor de como o código é formatado.

Você pretende ir primeiro de 500 filiais com muitos arquivos diferentes, para a maioria das filiais, tendo apenas alguns arquivos diferentes. Enquanto ainda ganha dinheiro suficiente para viver.

Você ainda pode ter 500 filiais em muitos anos, mas se elas forem muito mais fáceis de gerenciar, então você ganhou.

Baseado no comentário de br3w5:

  • Você poderia ter cada turma diferente entre os clientes
  • Crie uma "xxx_baseclass" que defina todos os métodos que são chamados na classe fora dela
  • Renomeie a classe para que xxx seja chamado xxx_clientName (como subclasse de xxx_baseclass)
  • Use a injeção de dependência para que a versão correta da classe seja usada para cada cliente
  • E agora para o insight inteligente que br3w5 criou! Use uma ferramenta de análise de código estático para encontrar o código duplicado e mova-o para a classe base, etc.

Somente faça o que foi mencionado acima depois de ter conseguido o grão fácil, e trace-o com algumas classes primeiro.

    
por 09.11.2015 / 19:56
fonte
40

No futuro, faça as perguntas do Teste de Joel em sua entrevista. É mais provável que você não entre em um trembo de trem.

Este é um, ah, como deveríamos dizer ... realmente, realmente problema ruim a ter. A "taxa de juros" sobre essa dívida técnica será muito, muito alta. Pode não ser recuperável ...

Qual é a integração com o "núcleo" dessas mudanças personalizadas? Você pode criar sua própria biblioteca e ter um único "core" e cada cliente específico ter seu próprio "add-on"?

Ou todas essas configurações são muito pequenas?

Acho que a solução é uma combinação de:

  • Alterando todas as alterações codificadas em itens com base na configuração. Nesse caso, todos têm o mesmo aplicativo principal, mas os usuários (ou você) ativam / desativam a funcionalidade, definem os nomes, etc., conforme necessário
  • Movendo funcionalidades / módulos "específicos do cliente" para separar projetos, então em vez de ter um "projeto" você tem um "projeto principal" com módulos que você pode adicionar / remover facilmente. Como alternativa, você também pode fazer essas opções de configuração.
Nem será trivial como se você tivesse mais de 500 clientes, você provavelmente não fez nenhuma distinção real nisso. Espero que suas mudanças na separação desta tarefa sejam muito demoradas.

Eu também suspeito que você terá problemas significativos para separar e categorizar facilmente todo o seu código específico do cliente.

Se a maioria de suas alterações incluir diferenças de palavras específicas, sugiro ler perguntas como este sobre localização de idiomas. Se você está fazendo vários idiomas inteiramente ou apenas um subconjunto, a solução é a mesma. Este é especificamente PHP e localização.

    
por 09.11.2015 / 17:13
fonte
17

Este é um dos piores anti-padrões que você pode atingir com qualquer VCS.

A abordagem correta aqui é transformar o código personalizado em algo orientado pela configuração e, em seguida, cada cliente pode ter sua própria configuração, seja codificada em um arquivo de configuração ou em um banco de dados ou em outro local. Você pode ativar ou desativar recursos inteiros, personalizar a aparência das respostas e assim por diante.

Isso permite que você mantenha uma ramificação principal com seu código de produção.

    
por 09.11.2015 / 17:03
fonte
13

O objetivo das agências é explorar uma possível via de desenvolvimento sem arriscar quebrar a estabilidade do ramo principal. Eles devem ser mesclados em um momento adequado ou descartados se levarem a um beco sem saída. O que você tem não são muito ramificações, mas muito mais do que 500 forks do mesmo projeto e tentar aplicar os conjuntos de alterações vitais a todas elas é uma tarefa sísifa.

O que você deve fazer é ter seu código principal em seu próprio repositório, com os pontos de entrada necessários para modificar o comportamento através da configuração e para injetar o comportamento conforme permitido por dependências invertidas .

As diferentes configurações que você tem para os clientes podem então distinguir-se entre si por algum estado configurado externamente (por exemplo, um banco de dados) ou, se necessário, viver como repositórios separados, que adicionam o núcleo como um submódulo.

    
por 09.11.2015 / 18:08
fonte
7

Todas as coisas importantes foram propostas por boas respostas aqui. Eu gostaria de acrescentar meus cinco centavos como uma sugestão de processo.

Gostaria de sugerir que você resolva esse problema em um intervalo de longo ou médio prazo e adote sua política, como você desenvolve o código. Tente se tornar uma equipe de aprendizado flexível. Se alguém tiver permissão para ter 500 repos em vez de tornar o software configurável, então é hora de se perguntar como você trabalhou até agora e o que você fará a partir de agora.

O que significa:

  1. Esclareça as responsabilidades do gerenciamento de alterações: se um cliente precisar de algumas adaptações, quem está vendendo, quem está permitindo e quem decide como o código será alterado? Onde estão os parafusos para girar se algumas coisas devem ser mudanças?
  2. Esclareça o papel, quem em sua equipe tem permissão para fazer novas recompras e quem não é.
  3. Tente garantir que todos em sua equipe vejam a necessidade de padrões que permitam flexibilidade para o software.
  4. Esclareça sua ferramenta de gerenciamento: como você sabe rapidamente qual cliente tem quais adições de código. Eu sei, alguns "lista de 500" soa irritante, mas aqui está alguma "economia emocional", se você quiser. Se você não puder informar as mudanças do cliente rapidamente, você se sentirá ainda mais perdido e empatado como se tivesse que iniciar uma lista. Em seguida, use essa lista para agrupar os recursos da maneira que as respostas de outras pessoas mostraram:
    • agrupar clientes por alterações menores / principais
    • agrupar por assunto, alterações relacionadas
    • agrupar por alterações fáceis de mesclar e alterações difíceis de mesclar
    • encontre grupos de mudanças iguais feitas em vários repositórios (ah, sim, haverá alguns).
    • talvez o mais importante para conversar com seu gerente / investidor: agrupe por alterações caras e mudanças baratas

Isso não significa de forma alguma criar uma atmosfera de pressão ruim na sua equipe. Eu sugiro que você esclareça primeiro esses pontos e, onde quer que você sinta o apoio, organize isso junto com sua equipe. Convide pessoas amigáveis para a mesa, a fim de melhorar toda a sua experiência.

Depois, tente estabelecer uma janela de tempo a longo prazo, onde você cozinha essa coisa em uma pequena chama. Sugestão: tente mesclar pelo menos duas reposições toda semana e, assim, remover pelo menos uma . Você pode aprender que, muitas vezes, pode mesclar mais de dois ramos, à medida que obtém rotina e supervisão. Dessa forma, em um ano você pode lidar com os piores (mais caros?) Ramos, e em dois anos você pode reduzir esse problema para ter um software claramente melhor. Mas não espere mais, pois no final ninguém vai "ter tempo" para isso, mas você é quem não permitirá mais isso, já que você é o arquiteto de software.

É assim que eu tentaria lidar com isso se estivesse em sua posição. No entanto, eu não sei como sua equipe aceitará tais coisas, como o software realmente permite isso, como você é apoiado e também o que você ainda precisa aprender. Você é o arquiteto de software - basta ir em frente: -)

    
por 10.11.2015 / 07:11
fonte
5

Contrastando todos os mais dissimulados, vamos supor uma necessidade comercial real.

(por exemplo, o entregável é o código-fonte, os clientes são da mesma linha de negócios e, portanto, concorrentes entre si, e seu modelo de negócios promete manter seus segredos em segredo)

Além disso, vamos supor que sua empresa tenha as ferramentas para manter todas as ramificações, ou seja, mão de obra (digamos 100 desenvolvedores dedicados à mesclagem, assumindo atraso de lançamento de 5 dias ou 10 devs supondo atraso de lançamento de 50 dias é OK), ou testes automatizados tão impressionantes que as mesclagens automatizadas são realmente testadas para especificação principal e especificação de extensão em cada ramificação e, assim, apenas as alterações que não se fundem "de forma limpa" requerem intervenção humana. Se seus clientes pagarem não apenas por personalizações, mas por manutenção, isso pode ser um modelo de negócios válido.

A minha pergunta (e sim) é, você tem uma pessoa dedicada responsável pela entrega para cada cliente? Se você é, digamos, uma empresa de 10.000 pessoas, pode ser o caso.

Isso pode ser feito pela arquitetura de plug-in em alguns casos, digamos que seu núcleo seja tronco, plug-ins podem ser mantidos no tronco ou ramificações e a configuração para cada cliente é um arquivo com nome exclusivo ou realizada no ramo de clientes.

Os plugins podem ser carregados em tempo de execução ou incorporados em tempo de compilação.

Realmente muitos projetos são feitos assim, fundamentalmente o mesmo problema ainda se aplica - mudanças básicas simples são triviais de integrar, mudanças de conflito devem ser revertidas ou mudanças são necessárias para muitos plugins.

Existem casos em que os plugins não são bons o suficiente, quando muitos internos do núcleo precisam ser ajustados para que a contagem da interface do plug-in seja muito grande para ser manipulada.

Idealmente, isso seria tratado pela programação orientada a aspectos , onde trunk é o código principal, e branches são aspectos (isto é, código extra e instruções de como conectar extras ao core)

Um exemplo simples, é possível especificar que o foo personalizado seja executado antes ou depois do núcleo klass.foo ou que ele seja substituído ou que o envolva e possa alterar a entrada ou a saída.

Há uma tonelada de bibliotecas para isso, no entanto, o problema dos conflitos de mesclagem não desaparece - as fusões limpas são tratadas pela AOP e os conflitos ainda precisam de intervenção humana.

Por fim, esse negócio precisa se preocupar com a manutenção de agências , ou seja, o recurso X específico do cliente é tão comum que é mais barato transferi-lo para o núcleo, embora nem todos os clientes estejam pagando por ele. ?

    
por 10.11.2015 / 16:50
fonte
3

Você não está resolvendo a causa raiz da doença observando o sintoma. Usar uma abordagem de 'gerenciamento de código' é sintomático, mas não resolverá as coisas a longo prazo. A causa raiz é a falta de recursos de produtos "bem gerenciados", recursos & suas extensões e variações.

Seu código personalizado não representa nada além das extensões dos recursos do produto & capacidades e campo de dados mudam em outros.

Até que ponto os recursos personalizados, quão diferentes, quão contextualmente semelhantes ou não serão muito úteis para "sanear" a base de código do seu produto.

Mais do que o código e a versão, este é um local em que o gerenciamento de produtos, a arquitetura do produto e a arquitetura de dados entram em ação. Sério.

Como, no final das contas, o código não é nada além de sua oferta de recursos e serviços de negócios e produtos para seus clientes. É por isso que sua empresa está sendo paga.

Obter um melhor controle sobre isso deve vir do ponto de vista dos recursos e não do código.

Você, sua empresa e seu produto não podem ser tudo para todos. Agora que você tem uma base de receita decente de 500 clientes, é hora de produzir o que você pretende ser.

E se você está oferecendo várias coisas, faria sentido modularizar suas capacidades de produtos de maneira organizada.

Qual será o tamanho dos seus produtos? Ou então isso levará a problemas de 'qualidade de serviço' & "diluição e fragmentação do produto" à medida que você avança.

Você será um CRM ou ERP ou solicitar processamento / envio ou Microsoft Excel?

Suas extensões existentes precisam ser atualizadas e harmonizadas, da mesma forma que um grande software importante atrai e mescla produtos adquiridos de uma inicialização.

Você precisará ter um mapa de pessoa com gerenciamento de produtos e arquitetura de dados strong, o seguinte:

  • Filial principal, seus recursos de produto e recursos básicos
  • recursos, tipos e variações de extensões personalizadas
  • Significado e variação de 'campos personalizados'

.. para criar roteiro de assimilação e harmonização de todos esses segmentos de produtos soltos / ramos no grande contexto de sua aplicação principal.

PS: Conecte-se comigo, conheço uma pessoa que pode ajudar você a corrigir isso:)

    
por 10.11.2015 / 06:21
fonte
-5

Eu posso me relacionar com isso. Eu tenho tomado muitos projetos. De fato, 90% do nosso trabalho de desenvolvimento está consertando essas coisas. Nem todo mundo é perfeito, então eu sugiro que você use o controle de versão da maneira correta e onde você está, se possível, você pode fazer o seguinte.

  • A partir de agora, quando um cliente solicitar uma atualização, mova-o para o novo repositório bifurcado.
  • Se você quiser mesclá-los para mestre, faça isso como a primeira coisa e resolva conflitos.
  • Em seguida, gerencie seus problemas e sprints com o repositório e mantenha aqueles em master que você deseja iniciar no master. Isso pode colocar mais pressão nos ciclos de lançamento, mas isso vai poupar você com o tempo.
  • Mantenha uma ramificação principal do repositório principal para novos clientes e o repositório principal deve ter apenas as ramificações em que você está trabalhando para futuros itens. As ramificações legadas podem ser excluídas assim que forem migradas para os repositórios do cliente.

Eu pessoalmente importei um repositório do GitHub com 40 filiais para o Bitbucket e criei 40 repositórios. Demorou apenas quatro horas. Esta foi a WordPress variações de tema, então o push and pull foi rápido.

Existem muitas razões para "não fazer certo da primeira vez", e eu acho que aqueles que as aceitam rapidamente e passam a "fazer isso certo desta vez" sempre seriam bem-sucedidas.

    
por 09.11.2015 / 22:01
fonte