Respostas:
Os sinalizadores de recursos são um dispositivo de engenharia que pode ser usado para evitar ramificações de longa duração e conflitos no desenvolvimento do produto. Aqui está como pode ser usado o contexto de uma linguagem orientada a objetos para ajudar os desenvolvedores a colaborar em um recurso específico do produto enquanto um lida com uma nova versão. Essa solução também pode ser usada em contextos não orientados a objetos, desde que exista uma noção de "interface". ( cf. sistema de módulos OCaml.)
Para fins de ilustração, assumimos uma ferramenta que apresenta relatórios sobre dados armazenados em um banco de dados. O código implementa uma classe DatabaseClient usada para executar solicitações. À medida que o conjunto de dados cresce, fica claro que algum layout de dados alternativo melhoraria o desempenho do aplicativo. Portanto, Alice desenvolverá uma nova versão do DatabaseClient capaz de recuperar dados das estruturas com layout aprimorado, enquanto Bob manterá o histórico DatabaseClient .
Com as etapas a seguir, Alice e Bob podem colaborar em ramificações de vida curta, minimizando seus conflitos.
Alice renomeia DatabaseClient para DatabaseClient_v1 e cria uma classe delegada chamada DatabaseClient que usa um objeto DatabaseClient_v1 e implementa uma interface chamada DatabaseClientInterface. (Se possível, esse DatabaseClientInterface deve ser um artefato de código, mas os idiomas tipificados por pato nem sempre oferecem suporte a isso.)
Bob revisa as alterações feitas por Alice em 1 e está ciente de que seu trabalho de manutenção deve ocorrer no DatabaseClient_v1 .
Alice introduz um novo sinalizador de configuração no aplicativo que governa o comportamento do delegado DatabaseClient e implementa um espaço reservado DatabaseClient_v2 , uma classe implementando o DatabaseClientInterface cujos métodos todos lançam uma exceção "Não implementado".
Depois disso, Alice e Bob podem colaborar sem sincronização explícita, porque o código gravado em suas respectivas iterações está sujeito ao DatabaseClientInterface . Isso minimiza o risco de um conflito resultante do trabalho simultâneo.
As iterações de Alice podem ser muito curtas, como implementar um teste, implementar um método ou mesmo parcialmente, porque na produção, o código não é selecionado para uso e não precisa ser totalmente funcional. O testinguite automatizado deve ser configurado para que o DatabaseClientInterface sempre use DatabaseClient_v1, enquanto Alice possa alternar facilmente para DatabaseClient_v2 ao executar o testinguite localmente - ou em uma configuração de IC personalizada. Quando tudo estiver pronto, um único commit poderá executar a alteração, atualizando o valor da configuração que governa o delegado DatabaseClient .
As etapas são bastante "fáceis"; para passar para um aplicativo de sinalizador de recursos, você precisa basicamente de duas coisas:
O sinalizador básico de recurso é ativá-lo / desativá-lo, mas rapidamente você deseja liberar um novo recurso de forma acelerada, por exemplo: 1 servidor em cada 5 que hospeda o aplicativo tem o recurso "ativado" para iniciar, você ativa o recurso em outro servidor, até que todos os servidores estejam "ativados".
Isso significa que você deve ter cuidado para que seu recurso seja compatível com o aplicativo sem ele (coluna extra no banco de dados, por exemplo).
Existem estruturas em várias línguas para evitar a reinvenção da roda, a agora não mantida pela Etsy's tem um leia - me interessante para explicar como funciona.
O mundo do software incorporado geralmente usa sinalizadores de tempo de construção, no próprio código do aplicativo ( instruções #define
/ #ifdef
, por exemplo) e / ou nos arquivos de configuração das ferramentas de construção ( makefile
por exemplo).
Os sinalizadores de compilação podem ser usados, de maneira semelhante, não apenas para recursos, mas também para todos os tipos de refatoração de código, migrações, suporte a depuração etc.). Eles permitem confirmar no ramo de integração alterações parciais ou não verificadas sem interromper a construção ou causar regressões nos recursos / projetos que já estão trabalhando no ramo. Excelente para correções de pontos de manipulação, juntamente com alterações de progresso grandes / arriscadas / lentas (que de outra forma exigiriam uma ramificação de vida longa) de maneira contínua.
Mas, além de verificar o código de ramificação já existente para regressões, também é possível executar verificações de progresso / estabilidade do novo código. Para isso, os sinalizadores de tempo de construção precisam ser alternados.
Uma maneira de alternar os sinalizadores seria usar, em um pipeline de verificação separado do sistema de IC da mesma ramificação (se houver suporte para essa funcionalidade), um arquivo de patch alternando o sinalizador - para ser aplicado a um espaço de trabalho separado antes do Construir. Um conjunto diferente de artefatos seria construído nesse espaço de trabalho e depois verificado.
Como alternativa, um ramo de recurso de longa duração pode ser extraído do ramo de integração principal, mas a única alteração nesse ramo de recurso seria o sinalizador alternado. Devido a essa pequena alteração, o ramo de recursos pode ser sincronizado automaticamente extremamente rápido - praticamente sombreando muito de perto o principal ramo de integração. Uma execução de IC separada nessa ramificação não precisaria mais de um patchfile preliminar. Seria trivial transportar esse ramo de recursos, mesmo por um longo período de tempo.
Também pode ser possível criar, no ramo de integração principal, novos artefatos de construção que realmente seriam apenas clones dos artefatos de construção existentes, mas com os sinalizadores alternados. Dessa forma, nem o arquivo de correção preliminar nem o ramo de recursos seriam necessários para verificar o novo código, diretamente no ramo principal.