Manter centenas de ramificações personalizadas sobre ramificação mestre

140

Atualmente, temos uma ramificação principal 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 tem alguma personalização para diferentes propósitos, cada um em uma filial separada. A customizaçã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 personalizadas e distribuímos para os clientes, periodicamente fornecemos novos recursos e atualizamos nossa filial principal, e gostaríamos de fazer alterações nas ramificações principais nas ramificações personalizadas para atualizar para a versão mais recente.

Infelizmente, isso geralmente resulta em muitos conflitos no código personalizado, e passamos muitas horas passando por cada ramificação para resolver todos os conflitos. Isso é muito ineficiente e descobrimos que erros não são incomuns ao resolver esses conflitos.

Estou procurando uma maneira mais eficiente de manter nossas ramificações de lançamento do cliente atualizadas com a ramificação mestre que resultará em menos esforço durante a mesclagem.

Fernando Tan
fonte
11
Desculpe por não dar uma resposta "você pode usar a ferramenta X", mas não há uma.
Lightness Races in Orbit em
3
Ou durante a compilação (que provavelmente é mais comum). Apenas .. não inteiramente bases de código separadamente.
Lightness Races em órbita em
15
@FernandoTan - Seu sintoma visível pode ser o código, mas a causa raiz da sua doença é a fragmentação do produto, a cura precisa vir do mapeamento do foco / capacidade do produto, não da limpeza do código - que acabará por acontecer. Eu detalhado mais na minha resposta - programmers.stackexchange.com/a/302193/78582
Alex S
8
Isso também pode ser um problema econômico. Você realmente ganha dinheiro com todos esses 500 clientes? Caso contrário, você deve exagerar no modelo de preços e rejeitar solicitações de alteração se o cliente não pagar uma taxa extra.
Christian Strempfer
13
Isso fez meu coração partir um pouquinho. Felizmente, outras pessoas já estão gritando as respostas certas - minha única recomendação adicional é que você escreva e envie ao TheDailyWTF.
Zxq9 11/11/2015

Respostas:

314

Você está abusando completamente dos galhos! Você deve ter a personalização baseada na flexibilidade do seu aplicativo, não no controle de versão (que, como você descobriu, não é destinado / projetado para esse tipo de uso).

Por exemplo, faça com que os rótulos dos campos de texto venham de um arquivo de texto, não seja codificado no aplicativo (é assim que a internacionalização funciona). Se alguns clientes tiverem recursos diferentes, torne seu aplicativo modular , com limites internos estritos controlados por APIs rígidas e estáveis, para que os recursos possam ser conectados conforme necessário.

A infraestrutura principal e todos os recursos compartilhados precisam ser armazenados, mantidos e testados apenas uma vez .

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

Raças de leveza em órbita
fonte
142
+1 em "Você deveria ter feito isso desde o início". Esse nível de dívida técnica pode destruir uma empresa.
Daenyth
31
@Daenyth: Francamente, com quinhentas filiais personalizadas, fico impressionado que ainda não o tenha feito. Quem deixa as coisas ficarem tão ruins? lol
Lightness Races in Orbit
73
@FernandoTan Estou tão, tão, tão triste por você ...
enderland
20
@FernandoTan: Eu também. :( Talvez você devesse ter feito mais perguntas na entrevista?;) Para deixar claro, o "você" na minha resposta é a organização. É uma abstração. Não pretendo atribuir culpa a indivíduos.
Lightness Races em órbita em
58
Primeiro, obtenha mais informações: permita que os desenvolvedores façam uma diferença entre a versão atual e a ramificação personalizada. Então você pelo menos sabe quais são as diferenças. Essa lista permite que você veja onde é possível obter a redução mais rápida de ramificações. Se 50 tiverem nomes de campo personalizados, concentre-se nisso e economizará 50 ramos. Então procure o próximo. Você também pode ter alguns que não são restauráveis, mas pelo menos a quantidade será menor e não crescerá mais quando você conseguir mais clientes.
Luc Franken
93

Ter 500 clientes é um bom problema. Se você gastou um tempo adiantado para evitar esse problema com filiais, talvez nunca tenha conseguido permanecer negociando por tempo suficiente para obter clientes.

Em primeiro lugar, espero que você carregue 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 precisar pagar para que suas personalizações sejam feitas novamente. Eu começaria encontrando todos os arquivos iguais em 95% dos seus ramos. Esses 95% são a parte estável do seu aplicativo.

Em seguida, encontre 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 campos de texto diferentes, você tem 1 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 desejar alterá-lo.)

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

Considere armazenar 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.

Sempre que você faz o check-in de um arquivo em uma ramificação, DEVE diferenciá-lo com main e justificar todas as alterações, incluindo espaços em branco. Muitas alterações não serão necessárias e podem ser removidas antes do check-in. Isso pode se dever apenas a um desenvolvedor com configurações diferentes em seu editor para saber como o código é formatado.

Seu objetivo é ir primeiro de 500 ramificações com muitos arquivos diferentes, para a maioria das ramificações com apenas alguns arquivos diferentes. Enquanto ainda ganha dinheiro suficiente para viver.

Você ainda pode ter 500 agências em muitos anos, mas se elas forem muito mais fáceis de gerenciar, você venceu.


Com base no comentário de br3w5:

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

Faça o que precede somente depois de obter a granulação fácil e acompanhe-a com algumas aulas primeiro.

Ian
fonte
28
+1 para tentar fornecer uma abordagem para o problema real
Ian
35
Fiquei realmente preocupado que você estivesse se parabenizando por sua resposta, até que percebi que você não era o mesmo @Ian que escreveu a resposta.
Theron Luhn
2
Talvez eles devem usar uma ferramenta de análise estática de código para diminuir o que partes do código são duplicados (após a identificação de todos os arquivos que são os mesmos)
br3w5
1
Também a criação de pacotes de versão para ajudar a pista equipa que cliente tem a versão do código
br3w5
1
Soa como uma maneira prolixo de dizer "apenas refatorar seu código"
Roland Tepp
40

No futuro, faça as perguntas do teste Joel em sua entrevista. Você provavelmente não entraria em um acidente de trem.


Este é, ah, como diremos ... realmente, um problema muito ruim de se ter. A "taxa de juros" dessa dívida técnica será muito, muito alta. Pode não ser recuperável ...

Quão integradas ao "núcleo" estão essas alterações personalizadas? Você pode torná-los sua própria biblioteca e ter um único "núcleo" e cada cliente específico ter seu próprio "complemento"?

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

Eu acho que a solução é uma combinação de:

  • Alterando todas as alterações codificadas permanentemente em itens baseados em configuração. Nesse caso, todos têm o mesmo aplicativo principal, mas os usuários (ou você) ativam / desativam a funcionalidade, definem nomes etc., conforme necessário
  • Movendo a funcionalidade / módulos "específicos do cliente" para separar projetos, para que, em vez de ter um "projeto", você tenha um "projeto principal" com módulos, 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 terminado aqui com mais de 500 clientes, provavelmente não fez nenhuma distinção real nisso. Espero que suas alterações ao separar isso sejam uma tarefa que consome muito tempo.

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

Se a maioria de suas alterações tiver diferenças de expressão específicas, sugiro ler perguntas como esta sobre localização de idioma. Esteja você fazendo vários idiomas inteiramente ou apenas um subconjunto, a solução é a mesma. Isso é especificamente PHP e localização.

enderland
fonte
1
Além disso, como essa será uma tarefa enorme (para dizer o mínimo), será um desafio significativo até convencer seus gerentes a dedicar grandes quantidades de tempo e dinheiro a esse problema. @FernandoTan Pode haver perguntas e respostas neste site que podem ajudar com esse problema específico.
Radu Murzea 11/11/2015
10
Que pergunta do teste de joel lhe diria que a empresa está abusando de agências?
SpaceTrucker
2
@ SpaceTrucker: Bem, "Você faz builds diários?" pode ter ajudado. Com 500 filiais, eles provavelmente não os possuíam ou podem ter mencionado que o fazem apenas para alguns ramos.
sleske
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, codificada permanentemente em um arquivo de configuração, 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 manter uma ramificação principal com seu código de produção.

Daenyth
fonte
3
Se você fizer isso, faça um favor a si mesmo e tente usar o padrão da Estratégia o máximo possível. Isso tornará muito mais fácil manter o seu código do que se você simplesmente seguisse adiante if(getFeature(FEATURE_X).isEnabled()).
TMN
13

O objetivo das filiais é explorar uma possível via de desenvolvimento sem correr o risco de quebrar a estabilidade da filial principal. Eventualmente, eles devem ser mesclados de volta em um momento adequado ou descartados se levarem a um beco sem saída. O que você tem não são muitos ramos, mas sim 500 garfos do mesmo projeto e tentar aplicar os conjuntos de alterações vitais a todos eles é uma tarefa sísifa.

O que você deve fazer é colocar 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 injetar o comportamento conforme permitido pelas dependências invertidas .

As diferentes configurações que você tem para os clientes podem simplesmente se distinguir 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.

back2dos
fonte
6
Você se esqueceu dos ramos de manutenção, que são basicamente o oposto dos ramos que você descreveu em sua resposta. :)
Leveza raças na órbita
7

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

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

Que significa:

  1. Esclareça as responsabilidades do gerenciamento de mudanças: se um cliente precisa de algumas adaptações, quem as vende, quem as permite e quem decide como o código será alterado? Onde estão os parafusos para girar se algumas coisas precisam ser alteradas?
  2. Esclareça a função, quem em sua equipe tem permissão para fazer novos compromissos e quem não é.
  3. Tente garantir que todos na sua equipe vejam a necessidade de padrões que permitam flexibilidade ao software.
  4. Esclareça sua ferramenta de gerenciamento: como você sabe rapidamente qual cliente tem que adoção de código. Eu sei, alguma "lista de 500" parece irritante, mas aqui está uma "economia emocional", se você quiser. Se você não pode informar as alterações do cliente rapidamente, sente-se ainda mais perdido e empolgado como se tivesse que iniciar uma lista. Em seguida, use essa lista para agrupar os recursos da maneira como as respostas de outras pessoas aqui mostraram:
    • agrupar clientes por pequenas / grandes mudanças
    • agrupar por alterações relacionadas ao assunto
    • agrupar por alterações fáceis de mesclar e alterações difíceis de mesclar
    • encontre grupos de alterações 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 tem a intenção de criar uma atmosfera de pressão ruim em sua equipe. Prefiro sugerir que você esclareça esses pontos primeiro e, onde quer que 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.

Em seguida, tente estabelecer uma janela de tempo a longo prazo, onde você cozinha essa coisa em uma pequena chama. Sugestão: tente mesclar pelo menos dois repositórios por semana e remova pelo menos um . Você pode aprender que, com frequência, pode mesclar mais de dois ramos, à medida que obtém rotina e supervisão. Dessa forma, em um ano você pode lidar com as piores (mais caras?) Filiais 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 "terá tempo" para isso, mas você é quem não permitirá mais isso, pois é o arquiteto de software.

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

peter_the_oak
fonte
2
Bons pontos sobre o tratamento das questões sociais / organizacionais que estão por trás dos problemas técnicos. Isso é muitas vezes esquecido.
sleske
5

Contrastando todos os negativistas, vamos assumir a necessidade real dos negócios.

(por exemplo, entrega é código fonte, os clientes são da mesma linha de negócios e, portanto, concorrentes entre si, e o 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 filiais, ou seja, mão-de-obra (digamos 100 desenvolvedores dedicados à mesclagem, assumindo um atraso de liberação de 5 dias; ou 10 desenvolvedores assumindo um atraso de liberação de 50 dias como OK), ou tais testes incrível automatizado que funde automatizados são verdadeiramente testado tanto a especificação do núcleo e especificação de extensão em todos os ramos, e, portanto, somente as alterações que não se fundem "limpa" exigem intervenção humana. Se seus clientes pagarem não apenas pelas personalizações, mas também pela manutenção, esse pode ser um modelo de negócios válido.

Minha pergunta (e não-dizer) é: você tem uma pessoa dedicada responsável pela entrega a cada cliente? Se você é, digamos, uma empresa de 10.000 pessoas, pode ser o caso.

Isso pode ser tratado pela arquitetura de plug-ins em alguns casos, digamos que seu núcleo seja tronco, os plug-ins podem ser mantidos em tronco ou ramificações e a configuração de cada cliente é um arquivo com nome exclusivo ou é mantida na ramificação do cliente.

Os plug-ins podem ser carregados em tempo de execução ou integrados em tempo de compilação.

Na verdade, muitos projetos são feitos assim, fundamentalmente o mesmo problema ainda se aplica - alterações simples e básicas são triviais para integrar, alterações de conflito devem ser revertidas ou são necessárias alterações em muitos plug-ins.

Há casos em que os plug-ins não são bons o suficiente, é quando tantos internos do núcleo precisam ser aprimorados que a contagem da interface do plug-in se torna muito grande para lidar.

Idealmente, isso seria tratado pela programação orientada a aspectos , em que tronco é o código principal e ramificações são aspectos (código extra e instruções sobre como conectar extras ao núcleo)

Um exemplo simples, você pode especificar que o costume fooé executado antes ou depois do núcleo klass.fooou que o substitui ou que o envolve e pode 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 - fusões limpas são tratadas pela AOP e os conflitos ainda precisam de intervenção humana.

Finalmente, esses negócios realmente precisam se preocupar com a manutenção das agências , ou seja, o recurso específico do cliente X é tão comum que é mais barato movê-lo para o núcleo, mesmo que nem todos os clientes estejam pagando por isso?

Dima Tisnek
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 para você a longo prazo. A causa raiz é a falta de recursos, recursos e suas extensões e variações 'bem gerenciados'.

Seu código 'personalizado' representa apenas extensões dos recursos e capacidades do produto e alterações no campo de dados de outras pessoas.

Quão extensos os recursos personalizados, quão diferentes, quão contextualmente semelhantes ou não, serão muito úteis para "higienizar" a base de código do seu produto.

Mais do que como você codifica e versão, este é um local onde o gerenciamento de produtos, a arquitetura de produtos e a arquitetura de dados entram em cena. A sério.

Porque, no final do dia, o código não passa de sua oferta de recursos / serviços comerciais e de produtos para seus clientes. É para isso que sua empresa está sendo paga.

A melhor maneira de lidar com isso deve vir do ponto de vista dos 'recursos' e não do ponto de vista 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ê estiver oferecendo várias coisas, faria sentido modularizar os recursos do seu produto de maneira organizada.

Quão amplo e profundo serão seus produtos? Ou então isso levará a problemas de "qualidade de serviço" e "diluição e fragmentação do produto" à medida que você avança na linha.

Você será um CRM ou ERP ou processamento / despacho de pedidos ou Microsoft Excel?

Suas extensões existentes precisam acumular e harmonizar, da mesma forma que uma grande empresa de software atrai e mescla produtos adquiridos em uma startup.

Você precisará ter uma pessoa forte de gerenciamento de produtos e arquitetura de dados para mapear o seguinte:

  • Filial mestre, seus recursos de produto e base de recursos
  • Recursos, tipos e variações de extensões personalizadas
  • Significado e variação de 'campos personalizados'

..para criar um roteiro de assimilação e harmonização de todos esses segmentos / segmentos de produtos soltos no grande contexto de seu aplicativo principal.

PS: Conecte-se comigo, conheço uma pessoa que pode ajudá-lo a corrigir isso :)

Alex S
fonte
-5

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

  • A partir de agora, quando um cliente solicitar uma atualização, mova-o para o novo repositório bifurcado.
  • Se você deseja mesclá-los para dominar, faça-o como a primeira coisa e resolva conflitos.
  • Em seguida, gerencie seus problemas e sprints com seu repositório e mantenha aqueles no master que você deseja iniciar no master. Isso pode colocar mais pressão sobre os ciclos de lançamento, mas economizará 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 nas quais você está trabalhando para coisas futuras. As ramificações herdadas podem ser excluídas depois de migradas para os repositórios do cliente.

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

Há muitas razões para "não fazer direito da primeira vez", e acho que aqueles que os aceitam rapidamente e passam a "fazer o certo desta vez" sempre teriam sucesso.

Farrukh Subhani
fonte
16
Como vários repositórios facilitariam a manutenção?
Mathletics
Em alguns casos como o nosso, os clientes precisam ter acesso a cada repositório e gerenciar seus próprios problemas quando se tornar uma solução personalizada, para que eles tenham seu próprio repositório, o que facilita o gerenciamento e, como eu disse, essas variações de tema do wordpress funcionaram bem. Pode não funcionar em muitos casos.
Farrukh Subhani