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.
fonte
Respostas:
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.
fonte
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:
Faça o que precede somente depois de obter a granulação fácil e acompanhe-a com algumas aulas primeiro.
fonte
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:
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.
fonte
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.
fonte
if(getFeature(FEATURE_X).isEnabled())
.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.
fonte
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:
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 :-)
fonte
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úcleoklass.foo
ou 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?
fonte
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:
..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 :)
fonte
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.
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.
fonte