Thursday, 29 March 2018

Estratégia de versão


Estratégia de versão
Obter através da App Store Leia esta publicação em nosso aplicativo!
Qual é a sua estratégia de versão do aplicativo? [duplicado]
Esta questão já tem uma resposta aqui:
Eu estaria interessado em obter as opiniões da comunidade SO sobre a melhor estratégia de versão de aplicativos.
Como você acompanha o número da versão do seu aplicativo? Você tem uma definição formal do que representa cada número / personagem dessa versão?
O que os diferentes números / strings da versão do aplicativo significam para seu aplicativo?
Você usa qualquer sistema de atualização automatizado em suas aplicações (por exemplo, algo como Sparkle) e como ele tem sido bom para você?
Você tem um procedimento de atualização separado para testadores beta ou testadores de pré-lançamento do seu aplicativo?
marcado como duplicado pelo mosquito, MichaelT, Kilian Foth, GlenH7, Rein Henrichs 29 de abril 13 às 2:42.
Esta pergunta foi feita antes e já tem uma resposta. Se essas respostas não respondem totalmente a sua pergunta, faça uma nova pergunta.
migrou do stackoverflow 18 de maio às 11h48.
Esta questão veio do nosso site para programadores profissionais e entusiasta.
Como você acompanha o número da versão do seu aplicativo? Você tem uma definição formal do que representa cada número / personagem dessa versão?
O que os diferentes números / strings da versão do aplicativo significam para seu aplicativo?
Eu uso o seguinte:
Major - A versão principal é um lançamento definitivo do produto. Aumentou quando há mudanças significativas na funcionalidade.
Menor - A versão menor é incrementada quando apenas foram adicionados novos recursos ou grandes correções de bugs.
Upgrade / Patch - Upgrade refere-se à substituição de um produto por uma versão mais nova do produto. Ele é incrementado somente quando a atualização é fornecida no release principal designado. A versão em patch começa com 0 e é incrementada somente quando o bug é resolvido.
Build No - Build Number é incrementado quando uma nova compilação é criada.
Você usa qualquer sistema de atualização automatizado em suas aplicações (por exemplo, algo como Sparkle) e como ele tem sido bom para você?
Nós usamos uma ferramenta de construção que automaticamente cria aplicativos à noite, que chamamos construção noturna e isso aumenta o número de compilação sempre que uma compilação é criada.
Você tem um procedimento de atualização separado para testadores beta ou testadores de pré-lançamento do seu aplicativo?
Não testadores testados durante a noite todas as manhãs que chamamos BAT (Build Aceceptance Test) e verificamos a construção noturna.
Permitam-me que note primeiro que parece não haver acordo sobre a "melhor" estratégia. Só posso compartilhar minha experiência em um projeto atual.
A versão do sistema é definida manualmente em uma propriedade de construção. Acontece quando a equipe concorda em uma nova versão. Como versão de versão adicional, usamos o número de compilação que é gerado automaticamente pela construção do CI.
Nós seguimos vagamente o esquema de nomenclatura do Ubuntu YY. MM. version. patch_buildNumber, já que descobrimos que o Major. Minor versioning atrapalha as expectativas do cliente;)
Não há atualizações automáticas, pois o aplicativo deve ser lançado por adminstrators.
Os lançamentos de teste são mais freqüentes do que as versões da GA, mas isso deve ser tudo.
Testei muitos sistemas de versões e agora estou muito feliz com este:
Major é definido manualmente e se refere a uma versão que inclui grandes melhorias O Menor também é definido manualmente e se refere a uma versão de atualização / manutenção, incluindo pequenas melhorias & amp; correção A revisão é gerada automaticamente e se refere a uma revisão exata no repositório.
O último nos permite ser muito flexíveis com o versionning. Podemos enviar várias versões para vários clientes e ainda assim poder depurar & amp; consertar com facilidade obtendo a versão específica dos repositórios e, em seguida, mesclar com o tronco.
A construção, embalagem e amp; A publicação é totalmente automatizada. A única ação manual é quando movemos o último pacote para o servidor de produção por FTP. Queremos manter o controle sobre isso para garantir que não entregamos lixo na produção. Ele vai para um estágio preliminar onde os primeiros usuários podem ler as notas de lançamento e decidir baixar e usar a versão. Os clientes que enfrentam erros específicos podem obter uma versão fixa muito rápido usando uma dessas versões.
Uso a versão semântica para as bibliotecas de código aberto e acho muito mais fácil trabalhar com outras bibliotecas que também o fazem. Ele fornece uma base comum para entender o que uma mudança de versão pode significar. A biblioteca ainda está em versão beta? É um lançamento apenas para correções de bugs? Haverá mudanças de API quebradas?
É basicamente uma codificação das melhores práticas de versão já utilizadas pela maioria dos projetos de código aberto.

Versão.
Crie uma linha de produtos.
O controle de versão é a prática de oferecer uma gama de produtos baseados em um produto principal. A versão pode gerar novos lucros e crescimento de duas formas distintas. Primeiro, oferecendo novos produtos para atender clientes com necessidades exclusivas. Drivers em climas quentes (que não requerem um grande número de amplificadores de arranque para iniciar um veículo) podem comprar a bateria de automóvel DieHard South de $ 79.99 da Sears. A Sears também oferece uma versão de platina de US $ 179,99 para drivers em áreas frígidas que precisam de uma bateria mais poderosa (amplificadores de inicialização mais altos). Segundo, oferecer bons, melhores e melhores produtos permite que os clientes escolham quanto devem pagar (a escolha de um consumidor revela sua verdadeira avaliação). O McDonald's vende US $ 1 McDoubles e US $ 3,59 Quarter Pounders. Na maior parte, os ingredientes nesses dois sanduiches são semelhantes. Os clientes sensíveis ao preço adquirem o McDoubles, enquanto aqueles com orçamentos maiores escolhem preços trimestrais de preço premium.
A criação de um produto versionado pode ser implementada através de três métodos principais:
Premium (maior qualidade, acesso garantido, mais rápido, serviço prioritário, baixa franquia / melhor cobertura). Descartado (menor qualidade, mais restrições, fora de pico, marca privada, desagregação, franquias mais elevadas e benefícios mais baixos). Conheça as necessidades exclusivas dos clientes (tamanho do pacote, garantias estendidas e aprimoradas, clubes mensais, agrupamento, plataformas, uso diferente).
A versão atrai novos clientes (com necessidades exclusivas), além de obter diferentes margens de lucro para diferentes clientes. Os produtos descontraídos oferecem descontos, enquanto os prémios são derivados de versões topo de linha. O aumento dos lucros dessas versões premium de alta margem e novos clientes (com necessidades exclusivas, bem como aqueles que compram versões com preços baixos) são um componente chave da campanha de arrecadações de preços de todas as empresas.

Estratégia de versão
Obter através da App Store Leia esta publicação em nosso aplicativo!
Estratégia de versão do CouchDB.
Seria a seguir uma estratégia viável para implementar o controle de versão (usando "exemplo" como tipo de documento de exemplo):
Tem um documento original em que o campo type é nomeado example_original.
As alterações subsequentes ao documento têm o tipo example_change e o id do documento example_original como uma chave. A mudança também carregaria um timestamp.
Mantenha um documento com o tipo example_current que é o resultado de example_original com todos os example_change "applied". Um novo documento example_change seria aplicado automaticamente a este documento.
Encontrar uma versão específica consistiria em recuperar o documento example_original e aplicar as alterações desejadas (principalmente até um determinado timestamp, mas também poderia ser uma série de alterações).
Devo mencionar que o meu caso de uso envolverá um número limitado de mudanças no original. A maioria das atualizações consistirá em novos documentos originais. Embora este seja o meu caso de uso atual, também estaria interessado em problemas que resultariam se muitas mudanças fossem envolvidas.
Que prós e contras você vê nesta abordagem?
Minha primeira preocupação é: ao "obter" uma determinada versão, você pode aplicar as alterações ao original sem modificar o banco de dados?
Você já precisará excluir algo do histórico? Você tem certeza? Realmente, com certeza? Como sobre filiais?
Em suma, isso parece uma estratégia complexa. Tenha em mente que eu ouvi sobre o CouchDB mas nunca o usei. Eu preferiria uma abordagem mais simples:
Quando você cria um documento, você atribui um UUID. Não use o nome ou você terá problemas durante as operações de renomeação. Adicione um campo de versão que lê "1". Crie um segundo documento que contenha uma lista de documentos com o mesmo UUID ou adicione um ponteiro "pai" ao primeiro documento.
Ter um "documento de histórico" por documento permite uma navegação mais rápida do histórico, mas os ponteiros pai são mais "seguros" (uma vez que você não pode facilmente criar estruturas ilegais com eles).
Quando você cria uma nova revisão, reutilize o UUID e atribua uma nova e única versão. Atualize o documento do histórico ou o ponteiro pai.
Esta estratégia é bastante simples de implementar e permite todos os tipos de flexibilidade mais tarde. Você pode apagar partes do histórico facilmente, renomear é simples e você pode criar ramificações.
A versão como abordagem de anexos descrita neste artigo deve atender aos requisitos da maioria das pessoas para controle de versão.
Qual é o status comercial desses documentos, especialmente legal? Eu trabalhei em situações em que sua proposta não seria apropriada de um negócio perseptivo, por causa da necessidade de provar que o documento apresentado como v.3 realmente é a versão 3 do documento. A aplicação dinâmica de deltas não cortaria a mostarda de conformidade.
Se, como você diz, as alterações aos documentos são pouco frequentes, então você não estará economizando muito espaço no disco armazenando deltas em vez de documentos completos. Armazenar documentos inteiros também permite a previsão confiável do tempo de recuperação para qualquer documento. Também reduz a complexidade do processo de recuperação.
Uma estratégia para o controle de versão com o CouchDB é NÃO compactar o banco de dados que contém os documentos para os quais você precisa manter um histórico completo. Você ainda pode compactar outros bancos de dados. Esta estratégia simples funciona hoje fora da caixa com uma estratégia de resolução de conflitos de edição.
A exclusão de um documento pode ser feita escrevendo uma nova versão sem conteúdo, exceto um conjunto de propriedades excluídas.
As ramificações não podem ser feitas dessa forma porque o mecanismo de controle de versão oferece um único encadeamento de revisões.
Agora, para o futuro possível do CouchDB:
Hoje, cada revisão contém uma cópia completa do documento, mas pode-se pensar que as otimizações do motor do CouchDB poderiam um dia armazenar deltas. Também é possível que no futuro o CouchDB ofereça uma API para impedir a compactação de certos tipos de documentos. Isso permitiria manter todos os documentos no mesmo banco de dados. Este seria um patch fácil para o CouchDB. Esta estratégia permite o gerenciamento de ramos de documentos, mas considerando a natureza do CouchDB como banco de dados de documentos, esta é uma possibilidade razoável, embora de longo prazo.

Em ALM com TFS / VSTS.
blogando sobre Gerenciamento do ciclo de vida do aplicativo com TFS / VSTS.
Estratégia de versionamento global & # 8211; AssemblyInformationalVersion.
Já ouviu falar de um terceiro atributo de versão (opcional) nos arquivos AssemblyInfo: AssemblyInformationalVersion. Não? Por favor leia!
Sem uma estratégia de numeração de versão metódica (montagem), a capacidade de determinar quais alterações foram incluídas em qual versão foi perdida. Na minha opinião, você sempre precisa saber exatamente quais arquivos de origem entraram em qual build e qual versão do software está implantada atualmente em um ambiente específico. Um sistema de numeração de versão aleatória cria confusão e, mais cedo ou mais tarde, causa riscos de implantação. Será um pesadelo buscar os arquivos de origem exatos para reproduzir um bug da produção.
Todo o controle de versão de assembléias que usam o tempo de execução de linguagem comum é feito no nível de montagem. A versão específica de um assembly e as versões de assemblies dependentes são registradas no manifesto do assembly. A política de versão padrão para o tempo de execução é que os aplicativos são executados apenas com as versões em que foram criados e testados, a menos que sejam substituídos pela política de versão explícita nos arquivos de configuração.
Cada projeto tem um arquivo AssemblyInfo que contém um atributo AssemblyVersion e um atributo AssemblyFileVersion.
AssemblyVersion: este é o número de versão usado pela estrutura durante a compilação e no tempo de execução para localizar, vincular e carregar as montagens. Quando você adiciona uma referência a qualquer montagem em seu projeto, esse é o número da versão que é incorporado. No tempo de execução, o CLR procura por assembly com este número de versão para carregar. Mas lembre-se que esta versão é usada juntamente com nome, chave pública chave e informações culturais somente se as assembléias tiverem o nome forte assinado. Se os assemblies não tiverem um nome forte assinado, somente os nomes de arquivo serão usados ​​para o carregamento. AssemblyFileVersion: Este é o número da versão dado a um arquivo como no sistema de arquivos. É exibido pelo Windows Explorer. Nunca é usado por framework ou tempo de execução para referência.
Mas e quanto a essa diferença entre AssemblyVersion e AssemblyFileVersion? Muitas vezes, vejo que a mesma versão é aplicada a ambos os atributos e # 8230; mas por que esses dois (diferentes) atributos são fornecidos pelo Framework? O AssemblyVersion deve ser a versão pública de um aplicativo de software inteiro, enquanto o AssemblyFileVersion é mais a versão de um componente específico que pode ser apenas uma pequena parte do aplicativo inteiro. O AssemblyFileVersion é o melhor lugar para colocar informações de versão de compilação extra que podem ser importantes para corrigir componentes individuais de um aplicativo de software.
Siga as recomendações da Versão Semântica para determinar como o AssemblyVersion deve ser atribuído e incrementado. Para o AssemblyFileVersion, tendem a incluir informações de compilação específicas. Muitas vezes, você precisará criar (e testar) um determinado número de vezes uma versão SemVer específica do seu software.
Por exemplo: o release 1 de um aplicativo de software pode ter o AssemblyVersion configurado como 1.0.0 (todos os componentes), enquanto o AssemblyFileVersion dos componentes individuais pode ser definido como 1.0.15234.2, que se refere a um número de compilação exclusivo do sistema de compilação e é vinculada a uma data específica e a uma revisão: & # 8220; 15 & # 8221; = ano de 2015; & # 8220; 234 & # 8221; = número do dia em 2015; & # 8220; 2 & # 8221; = segunda compilação processada nesse dia. Isso também permite posteriormente o patch de componentes individuais em produção com um similar AssemblyVersion (1.0.0), mas um AssemblyFileVersion diferente (1.0.15235.1).
Então, vamos tentar aplicar isso a um projeto de teste no Visual Studio e ver os detalhes da montagem depois de construir o projeto & # 8230;
Agora você deve estar confuso! Por que a versão do produto exibe o AssemblyFileVersion e onde está a AssemblyVersion? O problema aqui é que um novo projeto do Visual Studio não inclui um terceiro atributo de versão, AssemblyInformationalVersion, que deve representar a versão pública de todo o seu aplicativo de software. Observe que o CLR não se preocupa com esse terceiro atributo de versão (opcional). Em suma, as mesmas regras de versionamento semântico de AssemblyVersion devem ser aplicadas à AssemblyInformationalVersion.
Ah! Isso parece muito melhor, certo? Agora, também é fácil extrair esses metadados de seus assemblies implantados e esta informação pode ser bem listada na caixa sobre do seu software. O único problema com esta abordagem é que o AssemblyFileVersion não inclui o & # 8220; patch & # 8221; number (Version Semântica) do AssemblyVersion, mas isso pode ser ignorado com o fato de que o AssemblyFileVersion será exclusivo e pode ser vinculado a uma execução de compilação exclusiva no sistema de compilação. Esta maneira de trabalhar é minha interpretação pessoal de como o controle de versão pode ser aplicado corretamente em aplicativos de software complexos e não reflete as diretrizes oficiais da Microsoft. Meu objetivo aqui é fazer os desenvolvedores de software conscientes dos riscos potenciais de não ter uma estratégia de versão de versão clara.
Agora, esqueça a configuração manual das informações de versão nos arquivos AssemblyInfo e nunca libere o software de uma compilação local do Visual Studio. Em um processo de compilação simplificado, gerar números exclusivos de compilação e versão são coordenados de forma centralizada. Para uma solução eficaz de problemas e rastreabilidade, é imperativo que as montagens geradas sejam carimbadas com um identificador exclusivo que possa ser facilmente rastreado até um número de compilação do sistema.
Em uma próxima publicação, falo sobre como você pode alcançar essa estratégia global de versão com o novo sistema de compilação no TFS 2015.
Pós-navegação.
Deixe uma resposta Cancelar resposta.
O Real Semver permite uma tag de pré-lançamento como esta & # 8220; 1.2.4-beta04 & # 8221 ;. Semver também permite metadados como número de compilação, data, commit ou changeset como este & # 8220; 1.0.0 + 20130313144700 & # 8221 ;.
AssemblyInformationalVersion é o único campo que permite qualquer outra coisa que a notação de versão clássica, como 1.2.3.4. AssemblyVersion e AssemblyFileVersion só permitem a notação de versão estrita. E cada parte é limitada a 65536 eu acredito (por isso precisamos de notação estranha para datas certo?).
Você sugere configurar AssemblyInformationalVersion e AssemblyVersion para a mesma versão, mas depois perdemos esse poderoso recurso / ideia de semver. E tudo o que resta é colocar uma representação de data do número da compilação no AssemblyFileVersion (contanto que seja na forma de 1.2.3.4)
Talvez AssemblyInformationalVersion deve começar com AssemblyVersion, mas permitir que a tag e os metadados sejam anexados?
Ao empacotar um nupkg, a AssemblyInformationalVersion será usada. O que é ótimo porque dessa maneira você pode criar pacotes de pré-lançamento. Mas pode não ser fácil configurar tags de pré-lançamento para suas compilações automatizadas.
O mínimo que podemos dizer, é uma bagunça, é confuso e a estrutura não foi construída com tudo isso em mente. Para piorar as coisas, temos um trabalho real em vez de tentar descobrir esses detalhes inúteis 🙂
A informação de montagem global é uma boa idéia. A propósito, o Versionador de Montagem e o Info Updater podem ajudar as versões do projeto / montagem do gerenciador e outras informações para todos os projetos / montagens em uma única interface do usuário:
O Visual Smarter tem muito mais recursos do que os dois acima.
Compartilhar uma versão comum de montagem / arquivo de informações é uma boa idéia se todas as assembléias / projetos devem ter a mesma informação / versão. Caso contrário, o Assembly Versioner e Info Updater podem vir a resgatar no último minuto:
Visual Smarter tem muito mais recursos do que os dois acima.

Estratégias de versão de software.
Um perfeccionista preso no mundo real.
Software Versioning pode ser uma dessas áreas onde você nunca sente como se você tivesse exatamente isso. Não existe uma orientação definitiva com uma solução que satisfaça todos. Principalmente equipes de software estão confusas sobre o assunto, ou estão escolhendo ignorá-lo. Este guia visa preencher a lacuna e oferecer uma visão prática de várias estratégias populares e trade-offs.
Algumas das técnicas serão voltadas para a Microsoft stack (Windows), pois é o que tenho mais experiência, mas os princípios se aplicam em geral. Linux, Node. js, Python & amp; Ruby também são levemente tocados.
Versões em todos os lugares.
Estamos bastante acostumados com o termo "versão" hoje em dia. Mais comumente usado no mundo do software, vazou para a mídia e outras indústrias. Sequelas de filmes estão sendo versionadas - "Fast & amp; Furious 7" (7 !?), os sapatos estão sendo versionados - "Air Jordan XX8" e, mais popularmente, os livros estão sendo versionados - "One Minute Manager, 1984 edition". Na verdade, olhando para os livros, as pessoas estão sendo controladas há bastante tempo - "Enciclopédia Britânica", desde 1768 !.
A premissa é simples - à medida que os produtos sobrevivem e continuam sendo aperfeiçoados, os lançamentos mais recentes precisam ser diferenciados dos anteriores. O nome do produto não muda, porque o mercado já se familiarizou com ele, então algo é acrescentado no final para indicar que é mais novo (ou diferente).
Enquanto o controle de versão existia muito antes da era digital, o software realmente empurrou o problema para a frente. Modificar e liberar uma nova cópia de software é um processo muito rápido, muitas vezes mais rápido do que mudar uma linha de produção industrial para produzir uma nova peça de roupa ou imprimir uma nova edição de livro. Assim, os ciclos de iteração de software são muito mais curtos e um potencial para muitas edições simultâneas é muito maior.
Basta usar anos (ou mesmo meses), como em edições de livros, não é suficiente. Novas versões do software podem ser produzidas em poucos minutos. Além disso, o software possui um aspecto paralelo massivo - fluxos de software - onde várias versões principais podem existir, e todas podem ser continuamente atualizadas ao mesmo tempo. Isso raramente acontece com seus sapatos. (Eu queria fazê-lo, às vezes eu simplesmente não quero atualizar para o modelo de catálogo deste ano, quero uma melhoria para o meu antigo par!)
Por que versão?
Antes de mergulhar em como implementar o controle de versão, vamos parar e considerar por que queremos fazer isso em primeiro lugar! Afinal, se conhecemos as razões exatas de por que é útil, podemos avaliar melhor se as soluções propostas são adequadas.
Nós aludimos a isso na última seção, referindo-se ao chamado versão pública. Essa é a versão que é publicamente visível e, na maioria das vezes, tem peso de marketing (ou seja, é mais provável que seja definida pelo departamento de marketing / vendas). "Windows 7", "iPhone 5S", "Office 2013" - são exemplos de uma versão pública.
A versão pública destina-se a ser simples e memorável, indicando aos clientes que é novo e brilhante (assumindo que as pessoas geralmente querem "novo e brilhante"). As pessoas não entendem "10.6.6527.14789" - mas recebem "2013" ou "5". Tem sido cada vez mais popular usar o ano de lançamento como o número da versão pública, pois transmite de forma simples e poderosa o status atualizado. Os fabricantes de automóveis estão fazendo isso há muito tempo.
A versão privada é o que estamos acostumados no mundo do software. Um selo interno que (espero) identifica de forma exclusiva um determinado software. O software, como um carro, pode ser feito de muitas partes. Levando a analogia do carro ainda mais, a "versão privada" do carro é o número do chassi do VIN. Os fabricantes liberam e mantêm catálogos maciços de peças, mapeando para "números de versão" do carro. Um mecânico pode então pedir uma parte exata que corresponda ao seu veículo.
Sem um "número de peça particular", você não conseguiria consertar seu software na natureza, já que não saberia a "forma" exata que um módulo substituto deve ter para se encaixar no sistema como um todo. Imagine se você foi forçado a mudar seu carro inteiro quando uma luz da cauda quebrou.
Portanto, o número da versão privada é usado como um identificador de catálogo. Destina-se a ser usado quando solucionar problemas ou atender seu software. (Eu gosto da analogia "dogtag" de Jeff Attwood!) Ele deve mapear para uma descrição do que é essa peça de software - qual é a sua forma e função. E o que melhor "descrição" do que o próprio código-fonte original!
O uso essencialmente se resume a:
Identificando o código fonte original para uma peça de software, para permitir o patch incremental e para confirmar a operação defeituosa Identificando se uma parte é "compatível" com outra, ou se ela pode substituí-la.
Tudo isso é realizado com um número de versão particular. A versão pública é simplesmente um moniker de marketing, e mapeia para uma ou mais partes internas de software, cada uma com sua própria versão privada. (Assim como o Toyota Corolla 2011 contém um quadro ZRE142 e um conversor de torque 32000-12420)
Uso da versão.
No Windows, um conceito de número de versão é suportado por uma camada do sistema operacional. Os números de versão são incorporados em todos os arquivos executáveis ​​binários e podem ser vistos ao pairar sobre o EXE / DLL no Windows Explorer ou ao exibir Propriedades. Na verdade, qualquer arquivo que possa ter "recursos" pode ter uma versão, pois está armazenado no recurso VERSIONINFO.
Ele usa o formato comum em que todos nós somos usados ​​para: major. minor. build. revision (por exemplo, "1.2.360.0"). É importante notar que cada número é limitado a 16 bits e, portanto, não pode exceder 65535. Isso tem certas implicações sobre o que podemos representar com esses números.
Observe que um rótulo para esses números não é definido de forma estrita - são simples 4 números inteiros curtos. Os dois primeiros são referidos como maior e menor por unanimidade. Os dois últimos são onde vemos alguma variação, dependendo do esquema de controle de versão.
Esta versão é usada de forma proeminente durante o processo de atualização do Windows, que utiliza a tecnologia Windows Installer (MSI) para atualizar várias partes do sistema. Essencialmente, o Windows Installer segue certas regras para determinar se a atualização que está instalando é mais nova que o que já está instalado. Se a versão for maior, então está ok para atualizar.
Naturalmente, esse conceito flui para o Framework, que foi construído em torno de muitos conceitos existentes do Windows. Nós temos a classe Version, que segue o paradigma 4 integer. Também podemos definir AssemblyVersionAttribute e AssemblyFileVersionAttribute, que especificam uma versão de assembly e um recurso de versão do Windows, respectivamente.
Em, a versão do assembly existe separadamente da versão básica baseada no Windows VERSIONINFO, que é o que você vê no Windows Explorer (ou Propriedades do arquivo). Ele forma parte do nome forte da montagem e é usado exclusivamente pelo Framework ao resolver assemblies. A versão de duas montagens e a versão do arquivo do Windows - podem ser diferentes, mas, mais frequentemente, elas são as mesmas para evitar confusões.
usa a versão para rastreamento de dependência, ou seja, observando as versões de montagens a serem referenciadas, tornando assim óbvio quando uma atualização quebra a compatibilidade para aplicativos que dependem de uma determinada biblioteca. Este é um passo à frente da versão do arquivo nativo do Windows, que foi usada apenas durante o processo de atualização, e não ao referenciar uma biblioteca, levando ao infame "DLL Hell".
Vale a pena notar que a versão de s permite 4 inteiros de 32 bits, enquanto AssemblyFileVersionAttribute é limitado a 16 bits, como mapeia diretamente para o recurso VERSIONINFO. Portanto, se quisermos que AssemblyVersionAttribute e AssemblyFileVersionAttribute sejam os mesmos, isso também coloca um limite nos componentes da versão do assembly.
O Linux, em geral, usa um método diferente para endereçar o controle de versão. Os arquivos binários não possuem um selo de versão incorporado, como a maioria dos binários do Windows. Em vez disso, um nome de arquivo da biblioteca compartilhada indica sua versão, e. /usr/local/lib/mylib. so.1.5.
Um número de links simbólicos é criado, por ex. mylib. so - & gt; mylib. so.1 e mylib. so.1 - & gt; mylib. so.1.5. Um aplicativo pode fazer referência a uma biblioteca via link simbólico, como mylib. so.1, e obter a versão compatível 1.x mais recente instalada.
Isso funciona bastante bem, desde que todos sigam esta convenção. Cada biblioteca pode então, por sua vez, carregar bibliotecas, dependendo da mesma maneira.
Os usuários de Linux também estarão familiarizados com o popular "Advanced Package Tool", apt-get, usado de forma ubíqua nos sistemas derivados da Debian, como o Ubuntu. Sendo um verdadeiro Gerenciador de Pacotes, ele suporta a instalação de versões lado-a-lado e rastreamento de dependências entre pacotes. Examinamos as vantagens dos gerenciadores de pacotes nas seções a seguir.
Esquemas de números de versão.
Existem vários esquemas de numeração popular para software, mas todos eles são uma variação do mesmo tema e compartilham características comuns. Ter componentes principais e secundários da versão é o mesmo em toda a linha. O que eles representam é bastante consistente:
Major number increase: representa grandes mudanças no sistema de software, muitas vezes não compatíveis, ou adição de grande quantidade de novas funcionalidades. Menor aumento de número: representa mudanças evolutivas menos substanciais, principalmente atualizações ou melhorias na funcionalidade existente, ou adição de um novo menor conjunto de características.
Acima é apenas uma diretriz - não há regras estabelecidas sobre o que as versões maiores e menores devem representar. Só que eles devem aumentar à medida que mais recursos são adicionados ao software com o tempo.
Windows e binários especificam um esquema de versão de 4 partes: maior. menor. construir. revisão . Os dois últimos componentes são bastante livres, há muitas variações no que eles representam - alguns usam contadores de compilação incrementais, alguns usam data / hora da compilação e alguns os derivam dos números de revisão internos do controle de origem.
Muitos ignoram o número da revisão e focam apenas na compilação. O Windows Installer, por exemplo, possui apenas 3 componentes. Se você deseja que sua versão abranja binários e o pacote que contém, então é melhor limitar-se a apenas três números: maior. menor. construir.
Em qualquer caso, o padrão geral: quanto maior o número da versão, mais recente é o software.
Um popular sistema de controle de versão nos últimos anos (especialmente entre projetos de código aberto) foi apelidado de Versão Semântica (também conhecido como SemVer) e documentado no Semver. Ele apresenta alguns outros componentes, e torna a versão uma string alfanumérica, em vez de um número puro - abrindo algumas possibilidades interessantes.
Os três primeiros componentes são os mesmos que já discutimos, com o patch sendo opcional. O patch é praticamente equivalente ao componente de construção, mas a semântica pode ser diferente. A versão semântica realmente prescreve quando cada componente deve ser incrementado (com base em mudanças de "API pública").
O pré-lançamento, se especificado, é uma seqüência alfanumérica que é usada para marcar uma versão como uma que precede a versão final. Por exemplo, 1.3.567-rc1 irá preceder 1.3.567. Isso é útil para atribuir mais significado ao rótulo da versão do que simplesmente usar números.
Metadados é outro componente opcional, que permite marcação adicional do rótulo da versão (geralmente com um registro de data e hora de compilação), mas não participa da ordenação de versões, ou seja, versões que diferem apenas em metadados são consideradas as mesmas.
O pré-lançamento é útil com gerenciadores de pacotes como o NuGet, que os tratam de forma diferente - eles são considerados instáveis ​​e não são visíveis para o público em geral, a menos que seja explicitamente solicitado. Isso permite liberar versões alfa / beta sem afetar as pessoas que confiam em versões estáveis.
As tags de pré-lançamento também podem ser úteis no fluxo de lançamento interno ao lidar com hotfixes paralelos e compilações privadas, conforme discutido mais adiante neste artigo.
Versioning Non-Binary Files.
Então, sabemos como carimbar uma versão nos arquivos binários. Mas e os outros arquivos que compõem um sistema de software - arquivos de configuração, imagens, documentos, fontes, etc.? Como você estampa uma versão neles?
E quanto a frameworks da web como o ASP (ou Ruby, Node. js, Python, etc.), onde arquivos e páginas de origem podem ser modificados no local e atualizados automaticamente? Como podemos partilhar um sistema web, por exemplo, atualizar alguns arquivos de destino e ainda mantê-lo versionado?
A resposta é: não atualize arquivos individuais! Não há como manter um número de versão significativo para o seu aplicativo de software, se os arquivos não binários individuais podem ser atualizados ad-hoc como hotfixes.
Atualize usando um pacote em vez disso.
Importância da compilação e do pacote.
Quando você ouve o termo "build", normalmente a compilação vem à mente - a maioria das linguagens compiladas, como C #, C ++ ou Java, tem que ser compilada em um binário antes de poder ser executada. E, portanto, a construção é comumente associada ao processo de compilação.
Mas essa não é uma imagem inteira. Alguns idiomas ou frameworks, como Python ou ASP, não exigem estritamente a compilação. Eles podem ser interpretados, no caso de Python, ou compilados on-the-fly, no caso do ASP. O que uma build deve fazer para esses sistemas? Como você "constrói" um aplicativo Python?
É por isso que é mais útil pensar em construir como um processo de montagem ou simplesmente em uma embalagem. Assim como uma linha de bens de consumo, por ex. sapatos, é embalado antes do envio para as lojas, o mesmo acontece com um sistema de software, antes de ser lançado.
Um conceito de pacote é essencial para a versão, porque um pacote é uma coleção única das partes que compõem um sistema de software, ou parte dele, e, portanto, pode ser identificada e carimbada com uma versão. Com o sistema de gerenciamento de pacotes correto (que veremos na próxima seção), ele pode ser implantado e atualizado e especificar dependências nos outros pacotes.
O software hoje nunca é um único arquivo executável binário - é uma coleção de vários binários, bibliotecas, documentos, arquivos de configuração, imagens e outros recursos. Um pacote é o que nos ajuda a agrupá-los, versão e lançamento para o mundo exterior.
Um pacote não precisa ser sofisticado, embora ajude em algumas situações (por exemplo, bancos de dados). Pode até ser um arquivo ZIP simples, que pode conter uma versão no nome do arquivo ou incorporado como um arquivo de texto. Na verdade, muitos projetos de código aberto fazem exatamente isso: um lançamento é um ZIP ou um arquivo. tar. gz.
O importante é que um pacote é uma única unidade, que é lançada e atualizada ao mesmo tempo, levando a consistência. É comum ter vários pacotes, por exemplo, representando componentes "cliente" e "servidor", ou qualquer outro agrupamento lógico aplicável a um sistema de software. Cada pacote pode ser atualizado por conta própria.
Vamos dar uma olhada em alguns dos métodos de embalagem comuns, a abordagem de versão e para qual aplicativo eles são mais adequados.
Instalador do Windows.
Melhor Adequado: Aplicativos Completos de GUI do Windows, Serviços do Windows ou Drivers.
O mais antigo e, por muito tempo, o único caminho recomendado, para instalar aplicativos em uma plataforma Windows. Ele possui um suporte de versão incorporado e um conjunto de regras sofisticadas (alguns dirão "complicado") para determinar quando atualizar componentes. While a Windows Installer package (.msi) is a single file, in essence, it is a collection of small logical components (down to single files) that can be updated independently.
Windows Installer will actually check each individual file that is being installed, whether it has a version and whether the version is greater than a file with the same name already installed. That means it is important to version not just the installer package, but each file contained in it. But it also means that it is incredibly difficult to do downgrades (i. e. rollbacks) with Windows Installer.
It is best suited for traditional Windows Applications (GUI, services, drivers) that are released to the public. It is, however, not the best choice for internally developed & distributed applications, any kind of Web applications, or database systems.
It was also used to deploy distributable libraries (native DLLs) and COM objects, but with today's focus on , it is not the right mechanism for distributing libraries.
Web Deploy.
Best Suited : Web Applications (IIS, ASP)
Web Deploy technology was specifically designed for deploying and synchronizing applications on Microsoft IIS web servers. IIS Web Farm replication uses Web Deploy commands and packages behind the scenes to synchronize sites across a set of servers. IIS Manager has an extension (enabled by installing Web Deploy) to "Import Application", which can install or update a web application using a Web Deploy zip package.
Its biggest disadvantage is that it can only be used for web applications on Microsoft IIS platform, and the limited mechanism for customizing installation. While it could be suited for simple web applications, it can quickly become frustrating for anything more sophisticated, i. e. variables, conditional logic, databases, etc.
In addition, it has no inherent support for versioning .
Package Managers.
Best Suited : Shared Libraries, Dependencies, Command-line Utilities.
Package Managers are great for releasing and versioning shared components, and tracking dependencies between them. For example, if you have a shared library that you want others to use, then a Package Manager allows you to publish multiple versions side-by-side, and for consumers of the library to reference the version they depend on. Package Managers can resolve all inter-package dependencies, and retrieve only the versions that are expected. In effect, Package Managers solve the "DLL Hell" problem.
They are best used during development, to resolve library dependencies. However some Package Manager, like Chocolatey for Windows or apt-get for Ubuntu, are geared towards installing complete software.
Most importantly, Package Managers are designed around the versioning concept . So they are a perfect mechanism for distributing versioned software libraries.
For we have NuGet. A lot of open-source libraries have been published to its online repository, and it is now the defacto standard for distributing 3rd party components. It is encouraged that every team sets up their own NuGet repository to share and publish internally developed libraries in a versioned manner.
NuGet can even be used to release complete software systems - see next section.
Other development environments have their own - npm for Node. js, pip for Python, gems for Ruby, apt-get on Linux. Package Managers have been proven to be extremely useful, and have exploded in popularity.
Octopus Deploy.
Best Suited : Internally Developed & Deployed Software.
Octopus uses NuGet as the packaging and versioning shell. It is similar to an installer, only driven by PowerShell, meaning infinite flexibility in how the software is to be deployed. PowerShell already has a great support for configuring Windows Services, IIS Web Applications, Scheduled Tasks, SQL Server, and more.
For internally developed and distributed software (i. e. for a company running home-grown software solutions) this is a perfect release management vehicle. Packages are versioned and pushed to a shared NuGet feed (e. g. a network share), from where Octopus Deploy can release and deploy each package into the appropriate environment.
NuGet here plays a role of the application package/container, with a version stamped on it. Package can be built once, and then deployed as many times as needed to whatever environment.
Versioning & Packaging Databases.
Database versioning is one of the biggest challenges in software projects. Almost every team I encountered, either completely ignored it or had something inadequate in place. It certainly presents a challenge - database systems mix schema definition with actual live data , and there is no single "file" that can be effectively versioned.
We have to recognize the database as an integral part of the software system. One that executes on a proprietary 3rd-party platform (SQL Server, Oracle, PostgreSQL, etc), but the source of which is part of the software definition. It can be compared to script-based systems, such as Node. js or Python, only the scripts are written in a SQL dialect.
There are essentially three popular approaches to database versioning, that support automated deployments (I am not considering manual approaches, because they are error-prone, and have nothing to do with real versioning!).
DB - Migrations.
"Migrations" is a concept where developers keep a set of organized SQL script files, numbered sequentially, where each script applies modifications to the target DB to bring it to the expected state. Whenever a change is needed to the application database, a developer creates a new migration script that applies the delta changes.
All of the scripts are kept as part of the source control, and are packaged with the application (either embedded into the executable binary, or installed along-side). A migrations library then checks the target database for a dedicated table which holds the last "migration script number" applied, and then runs all the scripts with a number greater than that in order, effectively applying all of the changes in turn.
While this approach is simple to implement, and is favored among several popular frameworks (Ruby Rails, Entity Framework), it has a number of significant short-comings . Firstly, there is no single source view of all database objects (i. e. tables, stored procedures, etc), they are sprinkled through the multiple migration scripts. It is not clear which of the scripts contains which of the modifications. One has to "replay" them all to generate a database, and then look directly in the database (rather than source code).
Secondly, the migration scripts number becomes the "version" of the database, which is different from the software package version number for the rest of the application. This is somewhat confusing. In addition, this "version" does not really identify the state of the database, since a database can be changed outside an application without updating the "version". This may potentially break future installs, because migration scripts expect the database to be in a certain state to work.
Thirdly, developers have to be disciplined enough to follow the structure and apply ALL changes through migration scripts . Furthermore, when developing and debugging locally, one often has to go through several iterations before getting that table or store procedure change right. Yet only the final changes should make it into the migration script , meaning they have to be remembered and written manually. Otherwise, migration scripts would contain all of the intermediate changes made by all developers on the project. It is easy to see how that can grow out of proportion quickly.
Finally, there is an argument that migration scripts are a "history of changes", and it is a bit of a redundancy to store them in source control, which already is a "history" of code changes. We would be storing a history of a history . There's something philosophical about that.
Supported by some frameworks and libraries (Rails, DbUp, RoundHousE, EF Code First) Can work with any database Potentially high degree of control over SQL scripts.
Have to manually maintain all migration scripts Tracking changes through source control is difficult Not robust against target database out-of-band changes.
DB - SQL Compare.
Most often this is used in a manual approach, comparing a database between two environments (e. g. development vs test) to copy over the changes. We are considering an automated approach, suitable for the packaging and versioning strategies being discussed.
In source control, database is represent by a series of creation scripts (e. g. to create tables, stored procedures, triggers, etc), such that a new database with the right schema can be created from scratch. Usually each script file logically represents a corresponding object in the database, e. g. Table1.sql would be the create script for Table1 table. All of the scripts are included in the released package (sometimes even combined into a large single create script, by concatenating them).
The idea is that during automated package deployment a temporary fresh database copy is created, by running all of the creation scripts , and then a SQL Compare tool is executed to compare the pristine copy with the target database to generate a migration delta script on the fly.
The advantage of this approach is that it is robust against the target database out-of-band changes, since delta script is generated during deployment , rather than during development. SQL Compare tools (such a RedGate's SQLCompare or XSQL Compare) are sophisticated and mature enough tools that we can have some confidence in the generate SQL code. Each can be controlled by a multitude of options to fine-tune behavior with respect to renames, reordering columns, avoiding drops, etc.
In this case, target database is considered as a runtime environment , and we avoid having the issue of versioning it . Instead we version the package that contains all of the creation scripts , which is much easier, and use it to synchronize target database with what's expected in each version.
The big disadvantage of this approach is the difficulty of getting it right - there is no off-the-shelf framework that would support it, and it has to be developed. For SQL Server, read the next section for a better approach. For others, some day I may put together the set of scripts and logic necessary to achieve this, based on some of my prior work (unless someone else beats me to it).
Automatically detect and migrate changes, regardless of target DB state Only maintaining DDL (i. e. create) scripts in source control, meaning easy change tracking.
More difficult to setup, especially to be automated Having to create a temporary database during each deployment (need " create database " permission)
DB - DACPAC (SQL Server)
For SQL Server there is now a new recommended approach - DACPAC, and it can be produced by Visual Studio 2012 and above, if using the SQL Server database project. Really, this is a slick variation of the "SQL Compare" method above, just that Microsoft has done all the heavy lifting for you!
Essentially, DACPAC is a zip package which contains an XML schema model of what the target database should look like. It is compiled by Visual Studio based on the creation scripts in your project. In fact, it represents that temporary pristine database that we would have had to create manually. Only it is done automatically and the schema represented in an XML format. The real bonus is that a DACPAC can be versioned , i. e. its metadata supports storing a version number.
SQL Server Data Tools can be used to deploy a DACPAC package, which really performs a SQL Compare operation between the in-memory database model loaded from DACPAC and the target database. It does the same thing as SQL Compare, but avoids having to create the extra temporary database copy to do the comparison.
For applications having SQL Server as a back-end, a DACPAC can be included as one of the deployable packages, stamped with appropriate version generated during the build. Starting with SQL Server 2008 R2, database can be registered as a Data-Tier Application, and the latest DAC version is tracked in a system view that can be queried.
Can package the whole DB definition into a single package (or several packages) Can apply the same version to the package as the rest of the software system Same advantages as the SQL Compare method.
SQL Server only Need to treat lookup data in a special way (post-deploy MERGE script)
Build Auto-versioning.
Given the importance of consistent versioning discussed above, it makes sense to implement a strategy for automatically generating and stamping a version number during the software automated build process. We want the version number to be applied to the produced packages, and also applied to all the binaries generated through compilation.
There are several well-known and not so well-known ways of achieving this. We look at pros and cons of each.
Applying Build Number.
There are some who prefer to update the version number manually just before a release. I will argue that this is a bad practice. Firstly, it is easy to forget to do it, if you don't have an automated system for incrementing the version build number. And, if it is easy to forget, it will be forgotten at some point.
Secondly, without automatically updating build number, there will be multiple packages produced from the source code that have the same version number, but different functionality (as more commits are made to the source control). This will be confusing to say the least.
It is better to have a process, like ones described below, where version number build component is automatically updated whenever a non-local build is made.
Multiple Versions for Multiple Components.
If there are multiple software components, where each needs to have its own version number, then it is best to split them each into its own separate build. Don't mix multiple version numbers in the same build, as it unnecessarily increases the complexity, and raises a question about which of the build numbers should be used to label the build itself (in addition to having to tag each source sub-tree separately).
Developer vs Continuous vs Release Builds.
Release build is the one that will potentially be released to public or a particular environment - test, staging, production, etc. That's the build that needs to be consistently versioned to keep track of changes that are included and to link back to the source code at the time of compilation.
Note that the Release build can scheduled - it is popular to have a Daily or Nightly build. In most situations it should be the Release build, i. e. it should be versioned and packaged ready to be released.
Continuous Integration builds run whenever someone commits to the repository and are used to validate that the code compiles, and passes unit tests. There is no need to version this build, as it is not intended to be released.
Developers must also be able to do a Developer build , whether it is to test/fix the build process itself, or to generate shared software components to be used in development. Such builds are intended to be run locally only and should never be publicly released.
You can default the build part of the version number to "0". This will identify Developer builds, i. e. ones that are not supposed to be released. For Release builds pass the build number to your build scripts as a property. Have MSBuild stamp a version number on all generated assemblies and packages.
Tagging Source Control.
Since one of the primary reasons for having a version number is to be able to link back to source code used to build the software (see beginning of the article), it is important to create tags/labels in source control that identify the state of source code at the time that version was built.
Various systems call it differently - TFS has "Labels", Git has "tags". Tag should include the full version (including the build number) of the build, so that it can later be found, if needed.
Build Number - Version File Auto Increment.
Common technique is to record version number together with source code, usually in a separate file (e. g. "version. txt"). The build process then finds the file, reads the version, increments the build number portion, and commits the file back to repository.
If the commit message also includes the version number, e. g "Auto-increment: 1.3.156.0" , then it comes in handy when viewing commit history. You can see the changes that occurred between versions clearly by seeing the commits between the two "Auto-increment: . " messages.
This works fairly well, but has a few drawbacks. Mainly due to the fact that "version" becomes part of the source code. When merging changes between say release branch and main, you have to resort to "cherry-picking" (i. e. selecting just the code changesets) to avoid merging the modified version number. That requires being always careful, because you can accidentally change the versioning sequence of another branch just by merging the "version file" into it.
Control over the build number sequence (i. e. sequential) Can make it easy to see changes between versions in source control history.
Difficult to control merging between code branches in source control.
Build Number - External.
Overcoming the drawbacks of the auto increment approach, it is possible to track the build number outside of the source tree. Build server software such as CruiseControl or TFS Builds can do that - they track a build number internally for each "project" and are able to pass it as a parameter to MSBuild.
Version file is still used, but it records major and minor versions only, and doesn't have to change between each build. This makes it easier to merge changes from release branches back to main and others, since they will contain only code changes, without being intermingled with version increments. Major/minor version changes would occur early in the development cycle, when starting work on the next update, and are already set by the time release branch is created.
Not modifying source tree on every build makes merging between branches easier Versioned builds are forced to be built by a dedicated build server.
Relies on a build system that can supply a build number (e. g. CruiseControl, TFS Builds) Changing build number sequence can be difficult (e. g. TFS Builds)
Build Number - Derived from Date/Time.
A popular alternative is to derive build number for the date/time of the build. The advantage being that it carries more meaning (useful in diagnosis), and each build inherently should get a different build number (with later builds getting a higher number).
The trick, of course, is fitting all this into a 16-bit number, if using the standard 4-part Windows version number. While some solve it by using both, the build and revision components, I cannot recommend it, because revision cannot always be applied to external packages (like Windows Installer, or NuGet), which use only a 3-part version number.
This only allows only 4 unique builds per day, which is not a lot, unless all you want is a daily build .
Not depending on keeping track of the last build number Build number can be given more meaning, if it derives from a date.
Build number is not sequential (but it increases nevertheless) Limited to 16-bit (maximum 65535), so some overflow into revision (4th) number.
Build Number - Derived from Source Control.
A variation of the previous technique is to derive build number from a unique property in source control. With a centralized SCM like Subversion or TFS, a revision or changeset number is an ever increasing number that is tied directly to the source code. The big problem with it is that it can quickly overflow the 16-bit limit, meaning you may have to accept build numbers looping back to zero.
An alternative in distributed SCM, like Git, is to use the size of the commit history log as the build number. This will monotonously increase for any single branch, as new commits are made. It too can overflow the 16-bit limit, but goes a lot further than the global revision number.
Example: git rev-list HEAD --count.
Not depending on keeping track of the last build number No possibility of "forgetting" to update version file, or accidentally merge it to/from another branch.
Commit history size will grow beyond 65,535 at some point, overflowing the 16-bit build number.
Parallel Branches.
It's no secret that developing for multiple versions requires multiple branches in source control, each representing a "version" stream for the software. They can be roughly divided into:
Development branches - where unstable code for the next version lives, and where developers commit daily work Feature branches - veering off from development branches, encorporating larger feature development, that would otherwise disrupt other team members Release branches - representing versions of released software, or a release undergoing stabilization.
Each release branch needs to have an identifying version, and is usually named after it, e. g. "1.7" . A decision of whether to create a new release branch depends on how long it is expected that it will be in stabilization mode before releasing, and whether concurrent live versions are permitted (i. e. for packaged software). If you need to be able to maintain & hotfix the current released version, while a new version is being tested & stabilized, then create a new branch.
Development and feature branches need to have a version number that is above any of the existing release branches to avoid confusion. For example, if a 1.7 release branch is created, for the upcoming 1.7 release, then immediately update development branch version sequence to 1.8 .
Versioning feature branches is more difficult, since you don't want to start a new versioning sequence for every feature . Nothing should be "released" from feature branches, so this version is for internal purposes only. If using Semantic Versioning, attach a prerelease tag to clearly indicate this is a version for a feature branch, e. g. 1.8.781-dev-feature-x .
In any case, you wouldn't deploy anything built from a feature branch to the shared testing or production environment, or release a package from it. So it is acceptable to have version sequence overlap with that of development branch.
Finally, in the next section we look at how to version patches & hotfixes that are applied to release branches.
Handling Patches / Hotfixes.
Devising a system to handle patches depends heavily on the rest of the software development cycle, which is what many teams forget when searching for the "one, true way" of handling concurrent patching of the released/production software in parallel with working on the new version.
For example, having a short QA/test cycle, where most of the tests are automated, results in a more simplified and robust system, which does not have to deal with multiple parallel hotfixes "in test".
Overlapping hotfixes.
One difficulty that comes with managing parallel development is consistent versioning and deployment strategy that would overcome inherent conflicts. Consider following scenario: you have recently released a software package 1.5.167. Two urgent show-stopping issues have slipped past your QA process and now require a quick fix. You assign two developers to work on each one in parallel. How would they commit their fixes to minimize conflicts? How do you test each fix? How do you release one independent of the other?
This is a good example of the complexity of software release processes that can be encountered in real-world teams. It applies both to internal software and packaged software, but distribution of the hotfix might be slightly different for each one.
First, let's consider what happens if we remove concurrency . In the case where the two issues are worked one after the other , the solution becomes simple. The first fix gets committed into the maintenance/hotfix branch for 1.5 release stream, a new build is generated, with an incremented build number. Build goes through a quick QA cycle to make sure there is no regression, and then it is ready to be deployed. Same process repeats for the second fix.
The problem with concurrent approach is the time when development is in parallel, creating the entangled case where there is no build/package that contains only one of the fixes , i. e. independent of the other. This problem is magnified by a slow QA cycle , usually meaning there are no automated tests. While one fix is in test, if a commit for a second fix is made to the same branch, and a problem is discovered with the first one, it becomes very difficult to separate the two now.
The culprit here is, of course, the concept of a partial fix - the state where the fix is not complete. It has been committed, but has a problem with it, requiring further commits . This can easily create the case of a hotfix branch where the two fixes are "entangled" (quantum physics on the code level!).
Solution is to remove possibility of a partial hotfix .
This means that each hotfix has to be coded and tested in a separate code stream, independent of the other. Once tested, and ready for release, it is merged into the main hotfix release branch, where the automated build can create a new package and apply versioning (i. e. increment build number, for example, to 1.5.168).
Second hotfix, once tested, also has to be merged into the main hotfix release branch. But, because during the work on this second hotfix, the first hotfix got released, we first merge the first hotfix into the second hotfix's branch ! This ensures that we can test how the second hotfix operates, when applied on top of the first hotfix, and merge any code conflicts, if any.
In the end, you want a system with both hotfixes applied - that is the "next" version. So it makes sense that whatever hotfix is "second", it is applied on top of the "first" one. And creating a packaged release from the single hotfix release branch ensures that the version number is consistently incremented for the whole system.
Of course, above means that we must create a separate branch for each hotfix. Some version control systems, namely Git, make this very easy and part of the expected developer workflow. If you are using a version control system like TFS, then creating new branches for each hotfix is a bit more painful. In TFS, I suggest using named Shelvesets feature to emulate Git's process, and perform initial QA tests for a hotfix from a Shelveset-branch build. Then commit Shelveset into the hotfix branch to build the official hotfix package (and perform necessary merging).
What about the versioning of the interim hotfix builds ? The main hotfix release branch would have a standard versioning scheme applied (as discussed above), either incrementing a build number, or using a timestamp. Each new hotfix, applied on top of all previous hotfixes, gets an increased build number , and the software version keeps moving forward.
However, when building from the developer hotfix branch (or Shelveset in TFS), we also need to apply a version to distinguish it from other builds, and be able to deploy it into QA/test environment. We want to be able to test each hotfix in isolation, applied on top of an existing released version of the software system. This becomes problematic, if you have a single test environment .
You do not want to apply both hotfixes into one test environment, because there is no guarantee that they won't conflict or affect each other. If you are able to quickly spin up a test environment for a hotfix development branch, then you can truly parallelize team efforts. For a shared test environment, they have to be applied one at a time :
Force install latest release version (e. g. 1.5.168) to bring environment to a known state Install the hotfix version to be tested Perform the tests (preferably automated) For shared test environnments this is the bottleneck, since no other hotfixes can be tested at the same time (automation can help minimize the time spent in this step) Repeat 1-3, until tests are satisfactory.
What this means is that each hotfix has to have its build version number greater than the latest released version, the one it is being applied on top of. There are several ways to achieve that. If using a derived build number , this should just work out of the box. If incrementing or using external build numbers, then the easiest option is to simply force the build for hotfix development branch (or Shelveset) to use a number greater than latest released version (i. e. .168).
With Semantic Versioning, we can setup hotfix builds to use a "prerelease" tag that clearly marks it as a hotfix-test build. For example - 1.5.169-check14761 , where the trailing number could be a reference to the issue tracking system. This works especially well when using NuGet as the packaging mechanism.
Once tested, the changes can be merged into hotfix release branch, and an official build generated, with incremented build version number.
NOTE: Above process to resolve concurrent hotfixes is undoubtedly complicated. It is intended to solve a particular real-world scenario, but one that does not happen too often. If there are no concurrent fixes expected, you can simplify your life by applying fixes directly to the hotfix release branch.
Patching a large system.
If applying hotfixes to a large system, we don't want to upgrade the whole thing, which may involve a lot of different components - services, GUI applications, scheduled jobs, databases, etc. Instead, we want to apply the fix only to affected parts.
This is where splitting the system into multiple packages helps. Each corresponds to a logically contained piece of the system - for example, each service, application, database, etc is its own package. That means they can be patched independently by applying just that package .
Care must be taken about dependencies, if hotfix affects multiple packages at once. Although, in that case, ask yourself is it really a hotfix or a new minor version?
Patching for specific installation.
Some software shops may have developed the practice of patching the software for individual customers (for packaged software), in other words creating a "custom" version for just that installation, without including this fix in the rest of released software streams. This is one of the worst situations to be in, with regards to versioning, since it creates a large number of variations that have to be maintained separately.
Instead, release a general update , moving the overall software version forward for that release stream. Adopt a "feature" system , where parts of the software can be turned on & off based on configuration. If a specific fix is needed for a particular installation, then that code can be encapsulated behind a configuration switch which turns this section of the code on or off. That particular customer can turn it on , while the rest can have it off!
This is also a popular technique in web applications, of which only one installation exists (on the server), where various "features" can be enabled based on "configuration" for each user , or a set of users.
Patching the changes only.
There is often the temptation to simply patch in the changes to the live/production system by editing/replacing one file, or updating one table or stored procedure. The change is small, and it seems like the fastest way to solve the imminent issue, without changing anything else in the system.
While it seems like a smaller risk to make only the necessary updates directly, it makes it a whole lot harder to know the state of the system in the future. As more and more such "small" patches get applied, there is no longer any reliable way to link the running system back to the original source code, making further maintenance exponentially more complicated (and, ironically, increasing the risk).
Updating individual non-binary (e. g. config files) or altering database objects does not update any version number . That means it is difficult to tell which changes have been made to the system, leading to "maintenance hell" (a variation of the infamous "DLL Hell").
Rule of thumb: Any change to the system should change the version number.
NOTE : Windows Installer allows a so called "small update", where product version number does not have to change, used for small hotfix patches. I believe this creates too much confusion, and so I do not recommend it. Windows Installer does track each patch, through package code, so you always know which patches have been applied. But it means now having to track and remove patches on subsequent product updates, which complicates the process. It may work for Microsoft Windows and Microsoft Office, but I wouldn't recommend using it for any system.
Palavras finais.
This turned out to be a much longer article than I originally anticipated when I sat down to write about versioning . I am hoping it proves useful for software engineers out there looking for some guidance on how to apply these concepts in their own projects.
Still this seems like only a partial treatment of the topic.
Everything I wrote above has been learned through the painful process of trial & error over the years. If just a few readers have an "aha!" moment while reading this, then I have achieved my goal!

Versão.
DEFINIÇÃO de 'Versioning'
O Versioning é uma prática comercial em que uma empresa produz diferentes modelos do mesmo produto e, em seguida, cobra preços diferentes para cada modelo. A versão de um produto dá ao consumidor a opção de comprar um modelo de maior valor para mais dinheiro ou um modelo de menor valor por menos dinheiro. Dessa forma, a empresa está tentando atrair preços mais altos com base no valor percebido por um cliente.
Também conhecido como "discriminação de qualidade".
BREAKING DOWN 'Versioning'
Isso geralmente é feito quando um produto possui grandes custos fixos de produção e pequenos custos variáveis. Por exemplo, em pacotes de software, os recursos são adicionados ou removidos para dar diferentes versões e pontos de preço. Ter diferentes opções irá acomodar diferentes utilidades dos consumidores. Baseia-se na vontade de pagar do cliente; uma maior disposição de pagamento resultará na compra do produto de maior qualidade e uma menor disposição de pagamento resultará na compra do produto de menor qualidade.
Como o Versioning é aplicado.
O controle de versão pode ser encontrado em uma variedade de indústrias. No mercado de tecnologia de consumo, os tablets e os smartphones muitas vezes são lançados em diferentes versões, que podem ter variáveis ​​variáveis ​​de capacidade de armazenamento de dados e outras opções. Por exemplo, uma versão mais sofisticada de um smartphone pode incluir uma tela de resolução mais alta para reproduzir vídeo, bem como recursos de controle não encontrados nos outros telefones da linha de produtos.
Os conjuntos de software frequentemente veem opções de versão, o que permite que os clientes escolham os recursos que desejam pagar para acessar. Por exemplo, a Microsoft vende seu pacote de programas do Office em diferentes versões nas variações domésticas, pessoais e estudantis. A empresa também oferece o conjunto de software em diferentes níveis para usuários empresariais, cada iteração incluindo diferentes títulos de software, bem como serviços, dependendo da versão adquirida.
Os provedores de televisão por assinatura, que podem ser por cabo ou por satélite, também podem oferecer aos clientes versões diferentes de seus serviços, normalmente oferecidos como pacotes configurados a preços diferentes. À medida que mais canais são adicionados, o preço aumenta, com canais premium geralmente reservados para ofertas de pacotes mais dispendiosos.
A indústria automobilística aplica também o controle de versão aos seus produtos. O modelo base da maioria dos veículos pode ser equipado com recursos opcionais, como sistemas de som premium, conectividade com Internet e serviços de dados e serviços de bordo a bordo. O veículo pode ter opções de motor, permitindo que existam modelos mais rápidos. As opções de assentos também podem aumentar ou diminuir a capacidade do passageiro em diferentes modelos.

No comments:

Post a Comment