Estratégia de controle de versão
Obter através da App Store Leia esta publicação em nosso aplicativo!
Qual é uma boa estratégia de versão do aplicativo iOS ao usar o TestFlight para testes internos?
Eu tenho um aplicativo iOS que usa o controle de versão semântico para marcar compilações enviadas. Também estou usando o TestFlight da Apple para empurrar compilações internas para a equipe para testes / QA.
Empurrar uma compilação interna precisa carregar uma compilação para o iTunes Connect. Não há distinção entre uma compilação de teste e uma versão de lançamento para o iTunes Connect, e o iTunes Connect não permite substituir as versões carregadas anteriormente. Então, toda vez que eu quero empurrar uma nova compilação para teste interno, eu tenho que colidir o número da versão (bem, o número do patch (X. X. X).
Isso funciona bem, exceto para nossos usuários, parece que nossos números de versão saltam muito entre as atualizações. Por exemplo, se este é o nosso histórico de compilação:
. então os usuários estão apenas vendo os lançamentos em negrito, e nosso histórico de lançamento parece estranho:
Eu pensei que uma boa maneira de evitar isso é usar versões beta, como v1.1.0-beta para as compilações de teste, mas o iTunes Connect rejeita as cadeias de versões que não são X. X.X.
Existe uma maneira de continuar usando o TestFlight para testes internos / QA e evitar a aparência de um histórico de versões preenchido com lacunas aos usuários?
Eu uso um 4º número interno na versão de compilação, acredito que o iTunes ainda aceita isso. por exemplo. pode ser a versão 1.0.0, mas a compilação pode ser 1.0.0.87 indicando a 87ª compilação interna para testar. Você pode optar por diminuir o último número ao exibi-lo no aplicativo, mas as pessoas geralmente não se importam.
Descobriu isso fácil de entender e aceitar na maioria dos lugares.
O número de compilação em comparação com o número da versão não recebe crédito suficiente.
Estratégia de controle de versão
Obter através da App Store Leia esta publicação em nosso aplicativo!
Estratégia de versão para aplicativos no Git.
Minha empresa simplesmente começou a usar git e estou lutando com o controle de versão. Estamos usando a versão semântica (major. minor. patch), que definimos com base nas mudanças feitas no produto. No entanto, eu gostaria de usar a quarta parte para indicar o commit git. Eu li artigos sobre como fazer isso, especialmente um usando o gancho pós-mesclagem, mas quanto mais eu entrou nisso, mais confuso me tornei, não na parte técnica, mas na parte conceitual.
Eu realmente gosto da idéia de poder usar o hash de commit git na versão, pois deixa muito claro o que é o software. No entanto, não posso saber isso até depois do commit, em que ponto, alterar a versão exigiria outro commit e o hash seria incorreto. Então, se eu conseguir um número de versão, vá para git, eu realmente estaria recebendo o commit antes do que estava em uso. O que eu quero é poder ir ao git, pegar a etiqueta e criá-la exatamente como está em uso.
Eu suspeito que a resposta reside no processo de compilação, na medida em que esse processo iria consultar o git, obter o hash, atualizar AssemblyInfo. cs e depois construir o produto.
Como sou muito novo para tudo isso, eu me pergunto se:
Estou perdendo algo conceitualmente? Existe melhor maneira do que a construção? Se o processo de compilação for o caminho a seguir, existem maneiras comprovadas de lidar com isso?
O hash (para git, a sha1) selar todo o conteúdo do commit. Se apenas uma coisa mudar no commit, o conteúdo de um arquivo ou um metadata do commit (data de commit, commit message, author.), O hash será diferente. Nesse caso, você entende que não pode obter o hash de confirmação, modificar o conteúdo de um arquivo e alterar o commit e esperar ter o mesmo hash para o commit! Então, se você quiser criar suas assembléias, você deve atualizar o arquivo AssemblyInfo. cs com o hash de confirmação em seu processo de compilação antes de compilar a solução.
Para aqueles curiosos, esta é a solução que acabei por usar.
Depois de confirmar que eu entendi como funcionou toda a coisa commit / hash, comecei a procurar mais o processo de compilação. Acontece que foi bastante fácil. Este artigo me levou na direção certa. Aqui é o que eu fiz:
Como usar opções de ações para lucrar com o brexit.
Futuros de café Forexpros.
Estratégia de controle de versão do aplicativo.
Ele se concentra em torno do Git como a ferramenta para a versão do aplicativo do nosso código-fonte. Para uma discussão aprofundada sobre os prós e os contras do Git em comparação com os sistemas centralizados de controle de código fonte, consulte a web. Há muitas guerras de chama lá. Como desenvolvedor, prefiro o Git acima de todas as outras ferramentas em torno de hoje. Git realmente mudou a maneira como os desenvolvedores pensam em fusão e ramificação. Mas com o Git, essas ações são extremamente baratas e simples, e elas são consideradas uma das principais partes do seu fluxo de trabalho diário. Como conseqüência de sua simplicidade e natureza repetitiva, ramificação e fusão já não são algo para ter medo. Nós nos referiremos a este repositório, pois esse nome é familiar para todos os usuários do Git. Cada desenvolvedor puxa e empurra para origem. Mas, além dos relacionamentos centralizados push-pull, cada desenvolvedor também pode extrair as mudanças de outros pares para formar sub-equipes. Por exemplo, isso pode ser útil para trabalhar em conjunto com dois ou mais desenvolvedores em um novo recurso novo, antes de empurrar o trabalho em andamento para origem prematura. Na figura acima, existem subequipas de Alice e Bob, Alice e David, e Clair e David. No núcleo, o modelo de desenvolvimento é muito inspirado por modelos existentes por aí. O repo central possui dois ramos principais com uma vida infinita :. O ramo mestre na origem deve ser familiar para cada usuário Git. Paralelamente ao ramo mestre, outro ramo existe chamado desenvolvimento. É aí que são construídas todas as construções noturnas automáticas. Quando o código-fonte no ramo de desenvolvimento atinge um ponto estável e está pronto para ser lançado, todas as mudanças devem ser mescladas de volta ao mestre de alguma forma e depois marcadas com um número de lançamento. Como isso é feito em detalhes, será discutido mais adiante. Portanto, cada vez que as alterações são incorporadas de volta ao masterthis, é uma nova versão de produção por definição. Nós tendemos a ser muito rigorosos nisso, de modo que, teoricamente, poderíamos usar um script de gancho Git para criar e implementar automaticamente nosso software em nossos servidores de produção toda vez que houve um commit no master. Ao lado dos principais ramos, o mestre e desenvolvimento do modelo de desenvolvimento usa uma variedade de ramos de apoio para auxiliar o desenvolvimento paralelo entre os membros da equipe, facilitar o rastreamento de recursos, se preparar para lançamentos de produção e auxiliar na versão rápida de problemas de produção ao vivo. Ao contrário dos ramos principais, esses ramos sempre têm um tempo de vida limitado, já que serão removidos eventualmente. Cada um desses ramos tem um propósito específico e está vinculado a regras rígidas sobre quais ramos podem ser seu ramo de origem e quais ramos devem ser seus alvos de fusão. Vamos passar por eles em um minuto. Os tipos de ramificação são categorizados pela forma como os utilizamos. Eles são, naturalmente, antigos galhos Git. Os ramos de recursos ou às vezes chamados de ramos tópicos são usados para desenvolver novos recursos para a próxima ou futura versão futura. Ao iniciar o desenvolvimento de um recurso, a versão alvo em que esse recurso será incorporado pode muito bem ser desconhecida nesse ponto. A essência de um ramo de recursos é que ele existe enquanto o recurso estiver em desenvolvimento, mas acabará por ser incorporado de novo no desenvolvimento para adicionar definitivamente o novo recurso ao próximo lançamento ou caso de estratégia descartado de uma experiência decepcionante. Os recursos finais podem ser incorporados no ramo de desenvolvimento para definitivamente adicioná-los ao próximo lançamento :. A bandeira --no-ff faz com que a mesclagem sempre crie um novo objeto de confirmação, mesmo que a mesclagem possa ser executada com um avanço rápido. Isso evita perder informações sobre a existência histórica de um ramo de recursos e agrupa todos os compromissos que juntos adicionaram o recurso. No último caso, é impossível ver a partir do histórico do Git quais dos objetos de compromisso juntos implementaram um recurso; você teria que ler manualmente as mensagens do log do aplicativo. Reverter toda uma característica i. Sim, criará mais alguns objetos de confirmação vazios, mas a estratégia de ganho muito maior do que o custo. As filiais de lançamento suportam a preparação de uma nova versão de produção. Além disso, eles permitem pequenas correções de bugs e preparando meta-dados para um número de versão de versão, datas de compilação, etc. Ao fazer todo esse trabalho em um ramo de lançamento, o ramo de desenvolvimento é removido para receber recursos para a próxima grande versão. O momento-chave para se ramificar de um novo ramo de lançamento do desenvolvimento é quando o desenvolvimento quase reflete o estado desejado da nova versão. Pelo menos, todos os recursos que são direcionados para versões de versão a serem construídas devem ser incorporados para se desenvolver neste momento. Todos os recursos visados em lançamentos futuros não podem - eles devem esperar até que o ramo de lançamento seja derivado. É exatamente no início de um ramo de lançamento que o próximo lançamento recebe um número de estratégia - não anteriormente. Os ramos de lançamento são criados a partir do ramo de desenvolvimento. Por exemplo, diga a versão 1. Então, nos ramificamos e damos ao ramo de lançamento um nome que reflete o novo número de versão :. Depois de criar um novo aplicativo de ramificação para ele, nós superamos o número da versão. Isso pode, naturalmente, ser uma mudança manual - o ponto é que alguns arquivos mudam. Então, o número da versão colidível está comprometido. Este novo ramo pode existir lá por um tempo, até que a liberação possa ser lançada definitivamente. Durante esse período, correções de erros podem ser aplicadas nesse ramo em vez de no ramo de desenvolvimento. A adição de novos recursos novos aqui é estritamente proibida. Eles devem ser fundidos em desenvolver e, portanto, aguardar o próximo grande lançamento. Quando o estado do ramo de lançamento está pronto para se tornar uma versão real, algumas ações precisam ser realizadas. Primeiro, o ramo de lançamento é mesclado em mestre, uma vez que cada commit no master é uma nova versão, por definição, lembrar. Em seguida, esse commit no master deve ser marcado para uma estratégia futura fácil para esta versão histórica. Finalmente, as mudanças feitas no ramo de lançamento precisam ser incorporadas de volta ao desenvolvimento de que os lançamentos futuros também contenham essas correções de bugs. O controle de versão mantém as mudanças feitas no ramo de lançamento, precisamos combinar esses de volta no desenvolvimento. Este passo pode levar a um conflito de mesclagem provavelmente mesmo, já que mudamos o número da versão. Se sim, conserte e cometer. Os ramos do Hotfix são muito parecidos com os ramos de lançamento, pois eles também devem se preparar para uma nova versão de produção, embora não planejada. Eles surgem controlando a necessidade de agir imediatamente após um estado não desejado de uma versão de produção ao vivo. Quando um erro crítico em uma versão de produção deve ser resolvido imediatamente, um ramo de hotfix pode ser derivado da tag correspondente na ramificação mestre que marca a versão de produção. A essência é que o trabalho dos membros da equipe no ramo de desenvolvimento pode continuar, enquanto outra pessoa está preparando uma solução de produção rápida. Mas as mudanças no desenvolvimento de aplicativos ainda são instáveis. Podemos então ramo de um ramo de hotfix e iniciar o problema do aplicativo. Isso é completamente semelhante ao modo como os ramos de liberação são finalizados. A fusão posterior do bugfix no ramo de lançamento acabará resultando em que o bugfix seja incorporado ao desenvolvimento também, quando o ramo de lançamento estiver concluído. Se o trabalho em desenvolvimento imediatamente requer esse bugfix e não aguarde até que o ramo de lançamento seja concluído, você pode combinar com segurança o bugfix para desenvolver agora já. Forma um elegante modelo de versão que é fácil de compreender e permite aos membros da equipe desenvolver uma compreensão compartilhada dos processos de ramificação e liberação. Uma versão em PDF de alta qualidade da figura é fornecida aqui. Vá em frente e coloque-o na parede para obter uma referência rápida a qualquer momento. E para qualquer um que o solicitou: Vincent Driessen é um engenheiro e consultor de software Python independente da Holanda. Você pode contratá-lo. Posts domésticos sobre um modelo bem sucedido da estratégia de ramificação. Por Vincent Driessen na terça-feira, 05 de janeiro
Estratégia de versão para uma API interna complexa.
3 pensamentos sobre & ldquo; Versão de versão do aplicativo & rdquo;
A fonte mais abrangente de cartas escritas pelos membros do Congresso Continental.
Freethinkers começa com o debate feroz sobre omitir Deus da constituição, e passa dos abolicionistas, sufragetes, direitos civis e movimentos feministas para as batalhas contemporâneas sobre a influência religiosa no governo.
Tem sido um mistério fascinante toda a minha vida através da família.
Estratégia de controle 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 significam os diferentes números / strings na versão do aplicativo para o 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 significam os diferentes números / strings na versão do aplicativo para o 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 recente do product. It é incrementado somente quando a atualização é fornecida na versão principal designada. A versão do programa começa com 0 e é incrementada somente quando o erro foi resolvido.
Build No - Build Number é incrementado quando a 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 compilaçã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.
Seguimos vagamente o esquema de nomeação do Ubuntu YY. MM. version. patch_buildNumber, pois descobrimos que o controle de versão Major. Minor sujava as expectativas dos clientes;)
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:
O Major é configurado manualmente e se refere a uma versão que inclui importantes melhorias. Menor também é configurado manualmente e se refere a uma versão de atualização / manutenção, incluindo melhorias menores e 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 a versão. Podemos enviar múltiplas versões para vários clientes e ainda ser capazes de depurar & amp; conserte com facilidade obtendo a versão específica dos repos, então combine novamente 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 é um estágio preliminar em que os adotadores iniciais podem ler as notas da versão e depois 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.
O seu controle de versão da API é errado, razão pela qual eu decidi fazer 3 maneiras erradas diferentes.
No final, eu decidi que a maneira mais justa e equilibrada era irritar todos com igualdade. Claro que eu estou falando sobre o controle de versão da API e não desde o grande & # x201C; tabs versus spaces & # x201D; debate eu vi tantas crenças fortes em campos completamente diferentes.
Isso estava bem. Quando eu construí Eu fui escolhido? (HIBP) no final de novembro, pretendia ser um serviço simples e rápido que algumas pessoas usariam. Eu acho que é justo dizer que os dois primeiros pontos foram alcançados, mas não o último. Não foi & # x201C; alguns & # x201D ;, de fato, no final da primeira semana, foi mais do que o Google Analytics poderia lidar. O objetivo é que você sempre pode prever o futuro quando você escreve sua API e, em algum momento, você pode precisar mudar algo em que as pessoas já dependem.
Mas aqui, o problema & # x2013; Toda vez que você começa a falar sobre qualquer coisa a ver com APIs por HTTP, isso acontece:
Todo o caminho em que você se transforma há diferentes pontos de vista filosóficos em & # x201C; o caminho certo & # x201D; e muito retroceder e avançar no REST, o que é RESTful, o que não é e se importa. Deixe uma conversa sobre as mudanças da API, o impacto no controle de versão, por que há tantas idéias divergentes sobre como deve ser feito e, em última análise, por que nenhuma das palavras é tão importante como realmente conseguir coisas feitas.
Puxando mais dados de violação.
Tendo em mente que a mesma API é usada para o recurso de pesquisa no site e agora também por terceiros que criam tudo, desde aplicativos de smartphones até ferramentas de teste de penetração, a resposta acima funcionou bem no início, mas foi limitada. Por exemplo, essa resposta também não funciona bem:
Por quê? Porque & # x201C; BattlefieldHeroes & # x201D; é Pascal-encapsulado, o que é ótimo para combiná-lo com classes CSS codificadas (embora provavelmente não seja uma boa abordagem de longo prazo) e para ter um & # x201C; stable & # x201D; nome para se referir a (eu ganhei & # x2019; t mudá-lo, mesmo se houver uma segunda violação), mas não é adequado para exibição como título. Tudo isso sai do Azure Table Storage e depois entrei no SQL Azure para extrair dados relacionais que realmente descrevem a violação. Um dos atributos desse armazenamento relacional é o nome que você vê acima.
O que eu realmente queria fazer era algo mais parecido com isto:
Pegue? Para o ponto anterior sobre o nome da infração, esse ainda está lá no atributo de nome, mas agora temos um título também. Isto é o que você mostra às pessoas & # x2013; & # x201C; Battlefield Heroes & # x201D; & # x2013; Mas, mais importante ainda, se Gawker for repetidamente, eu posso nomear a violação de algo como Gawker2014 e o título pode ser algo amigável ao longo das linhas de & # x201C; Gawker (Syrian Electronic Army Attack) & # x201D ;. Ele segmenta o que é estável e previsível a partir do que não é e significa que as pessoas podem criar dependências, como imagens ou outros recursos no atributo de nome.
Os outros dados devem ser bastante claros: a data da violação, quando foi adicionada ao sistema, o número de contas, a descrição da violação (novamente, isso pode mudar se a verbiagem precisa ser modificada) e & # x201C; DataClasses & # x201D ;. Uma das coisas que muitas pessoas pediram foi uma descrição do que foi comprometido na violação, então agora há um monte de atributos que podem ser adicionados através de uma coleção sobre a violação em si. Eu já mostrando estes abaixo de cada violação na página dos sites Pwned (esta é outra razão pela qual agora posso ajustar algumas das descrições).
Esta é uma mudança radical. Embora o sentimento da API seja o mesmo & # x2013; forneça um nome de conta, receba uma lista de brechas & # x2013; não há mais um conjunto de caracteres de violação de nomes de violação. Se eu simplesmente substituísse a API antiga por essa, as coisas iriam quebrar. APIs. Devo. Evoluir.
O software evolui, as APIs devem ser controladas.
Deixe-se ficar claro sobre isso: o mundo continua. A API para o HIBP durou cerca de 2 meses, não porque estava mal concebida, mas porque o serviço tornou-se selvagem, inesperadamente bem sucedido. Eu gosto desse tipo de problema, e você também deveria.
Agora eu tinha uma escolha; Ou eu poderia me contentar com o que eu tinha e privar as pessoas de uma maneira melhor, eu poderia adicionar ao serviço existente de forma não rompente ou eu poderia criar uma nova versão (embora expondo a mesma entidade de uma maneira diferente) e construa da melhor maneira que eu sabia; sem bytes desnecessários, modelados corretamente (até que eu decida que uma nova versão é mais correta) e uma boa representação da entidade que eu finalmente tentarei entrar nos consumidores & # x2019; aplicativos.
Não há nada de errado em apresentar uma nova versão de uma API quando é a coisa mais sensível. Por todos os meios, faça o seu mais difícil para obtê-lo & # x201C; right & # x201D; desde o primeiro dia, mas faça isso com a expectativa de que & # x201C; right & # x201D; é um estado temporário. É por isso que precisamos ser capazes de versão.
Os vários campos de versão.
Certo, então, como é difícil esse negócio de versão? Quero dizer, deve ser um exercício simples, certo? O problema é que ele é muito filosófico, mas, em vez de ficar atolado, por enquanto, deixe-me esboçar as três escolas comuns de pensamento em termos de como elas são praticamente implementadas:
URL: Você simplesmente bateu a versão da API no URL, por exemplo: haveibeenpwned / api / v2 / breachedaccount / foo Cabeçalho de solicitação personalizado: Você usa o mesmo URL que antes, mas adicione um cabeçalho como o & # x201C; api-version: 2 & # x201D; Aceitar cabeçalho: você modifica o cabeçalho de aceitação para especificar a versão, por exemplo & # x201C; Aceitar: application / vnd. haveibeenpwned. v2 + json & # x201D;
Houve muitas, muitas coisas escritas sobre isso e eu vou ligar para elas no final da publicação, mas aqui a versão abreviada é:
Os URLs sugam porque devem representar a entidade: na verdade eu realmente concordo com isso na medida em que a entidade I & # x2019; m recuperando é uma conta quebrada, e não uma versão da conta violada. Semanticamente, não é realmente correto, mas sim, é fácil de usar! Os cabeçalhos de solicitação personalizados sugam porque ele não é realmente uma maneira semântica de descrever o recurso: a especificação HTTP nos dá um meio de solicitar a natureza que nós gostamos do recurso representado por meio do cabeçalho de aceitação, por que reproduzir esta? Aceite cabeçalhos sugam porque eles são mais difíceis de testar: eu não posso mais apenas dar a alguém um URL e dizer & # x201C; Aqui, clique neste & # x201D; antes eles precisam construir cuidadosamente a solicitação e configurar o cabeçalho de aceitação adequadamente .
Os vários argumentos para e contra cada abordagem tendem a passar de & # x201C; Este é o & # x2018; right & # x2019; maneira de fazê-lo, mas é menos prático & # x201D; através de & # x201C; Esta é a maneira mais fácil de criar algo consumível o que, portanto, o torna & # x2018; direito & # x2019; & # x201D ;. Há muita discussão sobre hipermídia, negociação de conteúdo, o que é & # x201C; REST & # x201D; e todas as outras questões. Infelizmente, isso muitas vezes se torna filosófico e perde a visão do objetivo real: construir um software que funcione e particularmente para uma API, tornando-o facilmente consumível.
É sobre ter um contrato estável, estúpido!
Mais importante do que todo o desencadeamento e delírio sobre fazê-lo dessa maneira ou dessa forma é dar às pessoas estabilidade. Se eles investem o seu código de escrita de esforço duro para consumir a sua API, então você melhorará muito, não vá e quebre-a mais na linha.
Honestamente, os debates sobre o que é & # x201C; RESTful & # x201D; versus o que não é como se o próprio termo ditar seu sucesso é apenas louco. Transforme essa discussão em & # x201C; Aqui estão os motivos práticos pelos quais isso faz sentido e isso é o que pode acontecer se você não fizer isso e # x201D ;, e eu e todos os ouvidos. O problema é que até mesmo as vozes da razão nas discussões barulhentas deixam dúvidas sobre o que realmente é a melhor abordagem e, portanto, eu alcancei um compromisso & # x2026;
Aqui estão 3 maneiras erradas de consumir a API HIBP que você pode escolher agora.
Ok, agora que nós estabelecemos claramente o que você faz é errado, eu gostaria de lhe dar a escolha de escolher entre qualquer uma das 3 maneiras erradas. Aguarde & # x2013; o que?! É assim: no entanto, implemento a API, será muito difícil de consumir, muito acadêmico, muito provável que falhe no proxy ou algo como algo. Ao invés de escolher 1 maneira errada, eu decidi você dar todas as 3 maneiras erradas e você pode escolher aquele que é o menos errado para você.
Wrong way 2 - cabeçalho de solicitação personalizado:
Inevitavelmente, alguém me dirá que fornecer 3 maneiras erradas é a coisa errada a fazer. Won & # x2019; t significa mais código kludge para manter? Não, simplesmente significa que a implementação da API da Web subjacente é decorada com dois atributos:
O primeiro é simplesmente uma restrição de roteamento que implementa RouteFactoryAttribute. Eu passo na rota e passa na versão que pode mapear para essa rota, então a implementação procura a presença de uma & # x201C; api-version & # x201D; cabeçalho ou um cabeçalho de aceitação correspondente a este padrão:
Se a versão especificada em qualquer uma dessas combina com a especificada na restrição de roteamento, então esse é o método que será invocado. Esta é uma adaptação simples desta amostra no CodePlex.
O segundo atributo que decora o método GetV2 acima vem com cortesia da Web API 2 e o recurso de roteamento de atributos. Claro que sempre poderíamos fazer o roteamento na API da Web, mas geralmente foi definido globalmente. O roteamento de atributos como esse traz a definição da rota para o contexto onde ela é aplicada e torna mais fácil ver qual ação do controlador vai ser chamada por qual rota. Isso também significa que as implementações para todas as 3 maneiras erradas de chamar a API se juntam em um local limpo.
Então, em suma, não, isso não cria muito kludge e é muito fácil de manter. Cada uma das 3 abordagens retornará exatamente o mesmo resultado e, o mais importante, eles permanecerão estáveis e ganharão mudar em qualquer maneira de quebrar e, no final do dia, isso é o máximo coisa importante, independentemente da opção que você escolher. A implementação inteira agora também está claramente documentada na página API do site.
Mas e se você não especificar uma versão?
Você conhece o pouco onde eu disse que você pode quebrar o que já existe? Sim, então isso significa que se você fizer o que faz agora e # x2013; não especifique uma versão & # x2013; então você obtém o que você recebe agora. Em outras palavras, nenhum pedido para uma versão específica significa que você obteve a versão 1.
Estou bem com isso, independentemente de ter alcançado este ponto por padrão. Eu sei que algumas pessoas sempre gostam de retornar a versão mais recente se um número não for especificado, mas IMHO que quebra todo o contrato & # x201C; estável & # x201D; objetivo; O que você obtém da API hoje pode ser completamente diferente do que você obtém amanhã se eu a revisar. Isso sugaria e isso quebraria coisas.
Você tem 3 opções, mas minha preferência pessoal é & # x2026;
Tenho o luxo de controlar a API e o principal consumidor de ser o próprio site da HIBP. Dado que eu forneci 3 opções para consumir a API, qual eu uso eu?
Eu fui com o favorito filosófico que é especificá-lo através do cabeçalho de aceitação. Eu não acho que isso é correto e os outros estão errados, e acho que isso faz mais sentido por duas razões principais:
Eu concordo que o URL não deve ser alterado: se concordarmos que o URL representa o recurso, a menos que tenhamos tentado representar diferentes versões do próprio recurso, então não, eu não acho que o URL deve mudar. As brechas para foo são sempre as brechas para foo e eu não acho que apenas porque eu mudo os dados retornados para que a localização do foo mude. Eu concordo que aceitar cabeçalho descreva como você gosta dos dados: Esta é uma semântica da especificação HTTP e assim como a semântica dos verbos de solicitação faz muito sentido (ou seja, estamos recebendo ou colocando ou excluindo ou postando) Da mesma forma, a maneira como o cliente gostaria que o conteúdo fosse representado.
De modo algum isso significa que eu acho que os outros dois estão errados e, francamente, não há melhor maneira de compartilhar a API com alguém do que dizer & # x201C; Aqui, clique neste & # x201D ;, mas quando eu puder construa facilmente a solicitação e gerencie os cabeçalhos, I & # x2019; fui com esta rota.
Na verdade, penso nisso, também uso a versão na rota do domínio. Por quê? Apenas através do processo de escrever esta API, eu estava constantemente me comunicando com as pessoas sobre as formas de consultá-lo (mais sobre isso mais tarde) e os atributos que retorna. Ser capaz de enviar um e-mail e dizer & # x201C; Ei, aqui, o que eu penso e # x201D; e eles simplesmente clicam nela e obtêm resultados inestimáveis. Este é o ponto que os proponentes da abordagem de versão de URL têm razão: você simplesmente não pode fazer isso quando você depende dos cabeçalhos.
Ah, e no caso de você voltar a verificar-me, no momento da escrita, eu ainda não enviei o site para v2 da API. Agora que os dados de violação são retirados na API quando ocorre uma pesquisa, significa que tenho o luxo de não carregar todas as violações na fonte na carga inicial (isso nunca mais será sustentável à medida que o conjunto de dados se expandir). Este salvará um monte de tráfego de saída e acelerará as coisas para as pessoas em termos de obter o site carregado, mas também significa apenas um pouco mais de trabalho no meu fim. Fique ligado.
No encerramento.
Claramente, eu fiquei um pouco embaixo da boca em relação a tudo o que está errado, mas, honestamente, quanto mais você lê sobre isso e quanto mais perguntas você faz, mais errado toda rota parece de uma forma ou de outra. Na verdade, eu sei muito bem que existem aspectos da minha implementação que serão referidos como & # x201C; wrong & # x201D; (Eu posso pensar em pelo menos um casal) e, naturalmente, estou me preparando para o ataque potencial de feedback para esse efeito. Entretanto, cada uma dessas opções funciona e, francamente, para todos os propósitos práticos, elas funcionam tão bem quanto entre si.
Se eu puder deixar outros considerando como versão suas APIs com um pensamento final: Ninguém usará sua API até que você a tenha construído. Pare de procrastinar. Nenhum destes são & # x201C; bad & # x201D; em qualquer sentido tangível, eles simplesmente são diferentes. Todos são facilmente consumíveis, todos retornam o mesmo resultado e nenhum deles provavelmente terá algum impacto real no sucesso do seu projeto.
Referências.
Sobrecarga de pilha: melhores práticas para o controle de versão da API? (ótima pergunta, ótimas respostas, fechadas como & # x201C; não construtivas & # x201D ;, eu assumi porque & # x201C; Bill the Lizard & # x201D; saiu no lado errado da cama naquela manhã) Lexical Scope blog: Como é REST APIs versão? (good comparison of versioning practices across services. albeit now a couple of years old) CodePlex: Routing constraint sample (linked in from Microsoft’s Web API page as an example of versioning APIs by adding a custom header) CodeBetter: Versioning RESTful Services (very pragmatic and a good description of the various ways an API might change) Vinay Sahni’s blog: Best Practices for Designing a Pragmatic RESTful API (he’s arguing for URL versioning for the sake of “browser explorability”) Pivotal Lans: API versioning (good view of the conflicting opinions out there) Web Stack of Love: ASP Web API Versioning with Media Types (good end-to-end walkthrough of creating an app to support versioning by content negotiation)
Hi, I'm Troy Hunt, I write this blog, create courses for Pluralsight and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals.
Hi, I'm Troy Hunt, I write this blog, create courses for Pluralsight and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals.
Próximos eventos.
I usually run private workshops around these, here's the upcoming public events I'll be at:
Don't have Pluralsight already? How about a 10 day free trial? That'll get you access to thousands of courses amongst which are dozens of my own including:
“The Cloud Never Goes Down”, Azure SLAs and other availability trivia.
Here’s how Bell was hacked – SQL injection blow-by-blow.
Subscribe Now!
Copyright 2018, Troy Hunt.
This work is licensed under a Creative Commons Attribution 4.0 International License. In other words, share generously but provide attribution.
Aviso Legal.
Opinions expressed here are my own and may not reflect those of people I work with, my mates, my wife, the kids etc. Unless I'm quoting someone, they're just my own views.
Published with Ghost.
This site runs entirely on Ghost and is made possible thanks to their kind support. Read more about why I chose to use Ghost.
Software Versioning Strategies.
A perfectionist stuck in the real-world.
Software Versioning can be one of those areas where you never feel like you got it exactly right. There is no definite guidance out there with a solution that would satisfy everyone. Mostly software teams are either confused about the subject, or are choosing to ignore it. This guide aims to fill the gap, and offer a practical look at various popular strategies and trade-offs.
Some of the techniques will be geared towards Microsoft stack (Windows, ), as it's what I am most experienced with, but the principles apply in general. Linux, Node. js, Python & Ruby are also lightly touched upon.
Versions Everywhere.
We are all pretty used to the term "version" nowadays. Most commonly used in the software world, it had leaked into the media and other industries. Movie sequels are being versioned - "Fast & Furious 7" (7!?), shoes are being versioned - "Air Jordan XX8", and, most popularly, books are being versioned - "One Minute Manager, 1984 edition". Actually, looking at books, people have been versioning for quite some time now - "Encyclopedia Britannica", since 1768!.
The premise is simple - as products live on and continue to be improved, newer releases have to be distinguished from the previous ones. Product name does not change, because market already became familiar with it, so something is appended at the end to indicate that it is newer (or different ).
While versioning existed long before the digital age, software really pushed the issue forward. Modifying and releasing a new copy of software is a very quick process, many times faster than it is to change an industrial production line to produce a new piece of clothing or print a new book edition. Thus software iteration cycles are much shorter , and a potential for a lot of concurrent editions is much greater.
Simply using years (or even months), as in book editions, is not sufficient. New versions of the software can be produced within minutes. In addition, software has a massive parallel aspect to it - software streams - where several major versions can exist, and all can be continuously updated at the same time. This rarely happens with your shoes. (I wish it did though, sometimes I just don't want to upgrade to this year's catalog model, I want an improvement to my old pair!)
Why Version?
Before diving into how to implement versioning, let's stop and consider why we would want to do it in the first place! After all, if we know the exact reasons of why it is useful, then we can better judge whether the proposed solutions are a fit.
We've alluded to this in the last section, referring to what's called a public version . This is the version that is publicly visible, and mostly carries marketing weight (i. e. it is most likely to be defined by marketing/sales department). "Windows 7", "iPhone 5S", "Office 2013" - are all examples of a public version.
Public version is intended to be simple and memorable, indicating to customers that it is new & shiny (assuming people generally want "new & shiny"). People don't understand "10.6.6527.14789" - but they get "2013" or "5". It has been increasingly popular to use the year of release as the public version number, as it simply and powerfully conveys up-to-date status. Car manufacturers have been doing it for a long time.
Private version is what we're used to in the software world. An internal stamp that (hopefully) uniquely identifies a particular piece of software. Software, like a car, can be made of many parts. Taking the car analogy further, car's "private version" is the VIN chassis number. Manufacturers release and maintain massive catalogs of parts, mapping to car "version numbers". A mechanic can then order an exact part that would fit your vehicle.
Without a "private part number", you wouldn't be able to service your software out in the wild, since you wouldn't know the exact "shape" that a replacement module has to be to fit into the overall system. Imagine if you were forced to change your whole car when a tail light broke.
Therefore, private version number is used just like a catalog identifier. It is is intended to be used when troubleshooting or servicing your software. (I like Jeff Attwood's "dogtag" analogy!) It must map to a description of what that software piece is like - what's its shape and function. And what better "description" than the original source code itself!
The use essentially boils down to:
Identifying original source code for a software part, to enable incremental patching and to confirm defective operation Identifying whether one part is "compatible" with another, or whether it can replace it.
All of this is accomplished with a private version number. Public version is simply a marketing moniker, and it maps to one or more internal software parts, each having its own private version . (Just like Toyota Corolla 2011 contains a ZRE142 frame and a 32000-12420 torque converter)
Version Use.
In Windows a concept of a version number is supported by a layer of the operating system. Version numbers are embedded into all binary executable files, and can be seen when hovering over EXE/DLL in Windows Explorer, or when viewing Properties. In fact, any file that can have "resources" can have a version, since it is stored in the VERSIONINFO resource.
It uses the common format we're all used to: major. minor. build. revision (e. g. "1.2.360.0"). It is important to note that each number is limited to 16-bit, and so cannot exceed 65535. This has certain implications on what we can represent with these numbers.
Note that a label for these numbers is not strictly defined - they are simple 4 short integers. The first two are referred to as major and minor pretty unanimously. The last two is where we see some variation, depending on the versioning scheme.
This version is most prominently used during the Windows Update process, which utilizes Windows Installer (MSI) technology to update various parts of the system. Essentially, Windows Installer follows certain rules to determine whether the update it is installing is newer than what's already installed. If the version is greater, then it's ok to update.
Naturally this concept flows to the Framework, which was built around many existing Windows concepts. We have the Version class, which follows the 4 integer paradigm. We can also define AssemblyVersionAttribute and AssemblyFileVersionAttribute , which specify an assembly version and Windows version resource respectively.
In , assembly version exists separately from the underlying Windows VERSIONINFO - based version, which is what you see in Windows Explorer (or file Properties). It forms part of the assembly strong name, and is used exclusively by the Framework when resolving assemblies. The two - assembly version and Windows file version - can be different , but more often they are the same to avoid confusion.
uses version for dependency tracking , i. e. noting the versions of assemblies being referenced, thus making it obvious when an update breaks compatibility for application that depend on a particular library. This is a step forward from native Windows file version, which was only used during the update process, and not when referencing a library, leading to the infamous "DLL Hell" .
It is worth noting that 's Version allows 4 32-bit integers, while AssemblyFileVersionAttribute is limited to 16-bit, as it maps directly to VERSIONINFO resource. Thus, if we want AssemblyVersionAttribute and AssemblyFileVersionAttribute to be the same, this effectively places a limit on assembly version components as well.
Linux, in general, uses a different method to address versioning. Binary files don't contain an embedded version stamp, like most Windows binaries do. Instead, a shared library filename indicates its version, e. g. /usr/local/lib/mylib. so.1.5 .
A number of symbolic links are created, e. g. mylib. so -> mylib. so.1 and mylib. so.1 -> mylib. so.1.5 . An application can reference a library via symbolic link, like mylib. so.1 , and get the latest 1.x compatible version installed.
This works fairly well, as long as everyone follows this convention. Each library can then, in turn, load libraries it depends on in the similar manner.
Linux users would also be familiar with the popular "Advanced Package Tool", apt-get, ubiquitously used on the Debian-derived systems like Ubuntu. Being a true Package Manager it supports installing side-by-side versions and tracking dependencies between packages. We take a closer look at advantages of Package Managers in the following sections.
Version Number Schemes.
There are several popular version numbering schemes for software, but all of them are a variation of the same theme and share common traits. Having major and minor version components is the same across the board. What they represent is fairly consistent:
Major number increase: represents major breaking changes in the software system, often not backwards compatible, or addition of large amount of new functionality Minor number increase: represents less substantial evolutionary changes, mainly updates or improvements in existing functionality, or addition of a smaller new feature set.
Above is only one guideline - there are no set rules about what major and minor versions are supposed to represent. Only that they are supposed to increase as more features are added to the software with time.
Windows and binaries specify a 4-part version scheme: major . minor . build . revision . The last two components are fairly free-form, there are many variations in what they represent - some use incremental build counters, some use date/time of the build, and some derive them from source control internal revision numbers.
Many ignore revision number, and focus only on build . Windows Installer, for example, only has 3 components. If you want your version to span both binaries and the containing package, then it is best to limit yourself to just three numbers: major . minor . build .
In any case, the general pattern : the greater the version number, the more recent the software is .
A popular versioning scheme in recent years (especially among open source projects) has been dubbed Semantic Versioning (aka SemVer), and documented at semver. It introduces a few other components, and makes version an alphanumeric string, rather than a pure number - opening a few interesting possibilities.
The first three components are the same as what we already discussed, with patch being optional. Patch is pretty much equivalent to the build component, but semantics can be different. Semantic Versioning actually prescribes when each component should be incremented (based on "public API" changes).
The prerelease , if specified, is an alphanumeric string that is used to tag a version as one that precedes the final release. For example, 1.3.567-rc1 will precede 1.3.567. This is useful to attach more meaning to the version label than by simply using numbers.
Metadata is another optional component, which allows further tagging of the version label (usually with a build timestamp), but it does not participate in version ordering, i. e. versions that only differ in metadata are considered the same.
Prerelease is useful with Package Managers like NuGet, which treat them differently - they are considered unstable and are not visible to general public, unless explicitly requested. This allows releasing alpha/beta versions without affecting those relying on stable releases.
Prerelease tags can also be useful in the internal release flow when dealing with parallel hotfixes and private builds, as discussed later in this article.
Versioning Non-Binary Files.
So we know how to stamp a version on the binary files . But what about the other files comprising a software system - configuration files, images, documents, fonts, etc? How do you stamp a version on them?
What about web frameworks like ASP (or Ruby, Node. js, Python, etc) where source files and pages can be modified in-place, and automatically updated? How can we patch a web system, i. e. update few target files, and still keep it versioned?
The answer is - don't update individual files ! There is no way for you to keep a meaningful version number for your software application, if individual non-binary files can be updated ad-hoc as hotfixes.
Update using a package instead.
Importance of Build and Package.
When you hear the term "build", normally the compilation comes to mind - most compiled languages, such as C#, C++ or Java, have to be compiled into a binary before being able to be executed. And so building is commonly associated with the process of compiling .
But that's not an entire picture. Some languages or frameworks, such as Python or ASP, don't strictly require compilation. They can be either interpreted, in Python's case, or compiled on-the-fly, in ASP's case. What should a build do for these systems? How do you "build" a Python app?
That's why it is more helpful to think of build as an assembly process , or simply packaging . Just like a line of consumer goods, e. g. shoes, gets packaged before shipping to the stores, so does a software system, before being released.
A package concept is essential to versioning , because a package is a single collection of the pieces that comprise a software system, or part of it, and can therefore be identified, and stamped with a version . With the right Package Management system (which we look at in the next section), it can be deployed and updated, and specify dependencies on the other packages.
Software today is never a single binary executable file - it is a collection of various binaries, libraries, documents, configuration files, images, and other resources. A package is what helps us group them together, version and release to the outside world.
A package doesn't have to be sophisticated, although it helps in some situations (e. g. databases). It can even be a simple ZIP file, that can contain version in the file name, or embedded as a text file. In fact, many open source projects do just that - a release is a ZIP or a. tar. gz archive.
The important thing is that a package is a single unit, that is released and updated at the same time, leading to consistency . It is common to have several packages, for example, representing "client" and "server" components, or any other logical grouping applicable to a software system. Each package can then be updated on its own.
Let's take a look at some of the common packaging methods, the versioning approach, and which application they are best suited for.
Windows Installer.
Best Suited : Complete Windows GUI Applications, Windows Services, or Drivers.
The oldest, and for a long time the only recommended way, to install applications on a Windows platform. It has a built-in versioning support and a sophisticated (some would say "complicated") set of rules for determining when to update components. 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!
Obter através da App Store Leia esta publicação em nosso aplicativo!
Estratégia de versão para aplicativos no Git.
Minha empresa simplesmente começou a usar git e estou lutando com o controle de versão. Estamos usando a versão semântica (major. minor. patch), que definimos com base nas mudanças feitas no produto. No entanto, eu gostaria de usar a quarta parte para indicar o commit git. Eu li artigos sobre como fazer isso, especialmente um usando o gancho pós-mesclagem, mas quanto mais eu entrou nisso, mais confuso me tornei, não na parte técnica, mas na parte conceitual.
Eu realmente gosto da idéia de poder usar o hash de commit git na versão, pois deixa muito claro o que é o software. No entanto, não posso saber isso até depois do commit, em que ponto, alterar a versão exigiria outro commit e o hash seria incorreto. Então, se eu conseguir um número de versão, vá para git, eu realmente estaria recebendo o commit antes do que estava em uso. O que eu quero é poder ir ao git, pegar a etiqueta e criá-la exatamente como está em uso.
Eu suspeito que a resposta reside no processo de compilação, na medida em que esse processo iria consultar o git, obter o hash, atualizar AssemblyInfo. cs e depois construir o produto.
Como sou muito novo para tudo isso, eu me pergunto se:
Estou perdendo algo conceitualmente? Existe melhor maneira do que a construção? Se o processo de compilação for o caminho a seguir, existem maneiras comprovadas de lidar com isso?
O hash (para git, a sha1) selar todo o conteúdo do commit. Se apenas uma coisa mudar no commit, o conteúdo de um arquivo ou um metadata do commit (data de commit, commit message, author.), O hash será diferente. Nesse caso, você entende que não pode obter o hash de confirmação, modificar o conteúdo de um arquivo e alterar o commit e esperar ter o mesmo hash para o commit! Então, se você quiser criar suas assembléias, você deve atualizar o arquivo AssemblyInfo. cs com o hash de confirmação em seu processo de compilação antes de compilar a solução.
Para aqueles curiosos, esta é a solução que acabei por usar.
Depois de confirmar que eu entendi como funcionou toda a coisa commit / hash, comecei a procurar mais o processo de compilação. Acontece que foi bastante fácil. Este artigo me levou na direção certa. Aqui é o que eu fiz:
Como usar opções de ações para lucrar com o brexit.
Futuros de café Forexpros.
Estratégia de controle de versão do aplicativo.
Ele se concentra em torno do Git como a ferramenta para a versão do aplicativo do nosso código-fonte. Para uma discussão aprofundada sobre os prós e os contras do Git em comparação com os sistemas centralizados de controle de código fonte, consulte a web. Há muitas guerras de chama lá. Como desenvolvedor, prefiro o Git acima de todas as outras ferramentas em torno de hoje. Git realmente mudou a maneira como os desenvolvedores pensam em fusão e ramificação. Mas com o Git, essas ações são extremamente baratas e simples, e elas são consideradas uma das principais partes do seu fluxo de trabalho diário. Como conseqüência de sua simplicidade e natureza repetitiva, ramificação e fusão já não são algo para ter medo. Nós nos referiremos a este repositório, pois esse nome é familiar para todos os usuários do Git. Cada desenvolvedor puxa e empurra para origem. Mas, além dos relacionamentos centralizados push-pull, cada desenvolvedor também pode extrair as mudanças de outros pares para formar sub-equipes. Por exemplo, isso pode ser útil para trabalhar em conjunto com dois ou mais desenvolvedores em um novo recurso novo, antes de empurrar o trabalho em andamento para origem prematura. Na figura acima, existem subequipas de Alice e Bob, Alice e David, e Clair e David. No núcleo, o modelo de desenvolvimento é muito inspirado por modelos existentes por aí. O repo central possui dois ramos principais com uma vida infinita :. O ramo mestre na origem deve ser familiar para cada usuário Git. Paralelamente ao ramo mestre, outro ramo existe chamado desenvolvimento. É aí que são construídas todas as construções noturnas automáticas. Quando o código-fonte no ramo de desenvolvimento atinge um ponto estável e está pronto para ser lançado, todas as mudanças devem ser mescladas de volta ao mestre de alguma forma e depois marcadas com um número de lançamento. Como isso é feito em detalhes, será discutido mais adiante. Portanto, cada vez que as alterações são incorporadas de volta ao masterthis, é uma nova versão de produção por definição. Nós tendemos a ser muito rigorosos nisso, de modo que, teoricamente, poderíamos usar um script de gancho Git para criar e implementar automaticamente nosso software em nossos servidores de produção toda vez que houve um commit no master. Ao lado dos principais ramos, o mestre e desenvolvimento do modelo de desenvolvimento usa uma variedade de ramos de apoio para auxiliar o desenvolvimento paralelo entre os membros da equipe, facilitar o rastreamento de recursos, se preparar para lançamentos de produção e auxiliar na versão rápida de problemas de produção ao vivo. Ao contrário dos ramos principais, esses ramos sempre têm um tempo de vida limitado, já que serão removidos eventualmente. Cada um desses ramos tem um propósito específico e está vinculado a regras rígidas sobre quais ramos podem ser seu ramo de origem e quais ramos devem ser seus alvos de fusão. Vamos passar por eles em um minuto. Os tipos de ramificação são categorizados pela forma como os utilizamos. Eles são, naturalmente, antigos galhos Git. Os ramos de recursos ou às vezes chamados de ramos tópicos são usados para desenvolver novos recursos para a próxima ou futura versão futura. Ao iniciar o desenvolvimento de um recurso, a versão alvo em que esse recurso será incorporado pode muito bem ser desconhecida nesse ponto. A essência de um ramo de recursos é que ele existe enquanto o recurso estiver em desenvolvimento, mas acabará por ser incorporado de novo no desenvolvimento para adicionar definitivamente o novo recurso ao próximo lançamento ou caso de estratégia descartado de uma experiência decepcionante. Os recursos finais podem ser incorporados no ramo de desenvolvimento para definitivamente adicioná-los ao próximo lançamento :. A bandeira --no-ff faz com que a mesclagem sempre crie um novo objeto de confirmação, mesmo que a mesclagem possa ser executada com um avanço rápido. Isso evita perder informações sobre a existência histórica de um ramo de recursos e agrupa todos os compromissos que juntos adicionaram o recurso. No último caso, é impossível ver a partir do histórico do Git quais dos objetos de compromisso juntos implementaram um recurso; você teria que ler manualmente as mensagens do log do aplicativo. Reverter toda uma característica i. Sim, criará mais alguns objetos de confirmação vazios, mas a estratégia de ganho muito maior do que o custo. As filiais de lançamento suportam a preparação de uma nova versão de produção. Além disso, eles permitem pequenas correções de bugs e preparando meta-dados para um número de versão de versão, datas de compilação, etc. Ao fazer todo esse trabalho em um ramo de lançamento, o ramo de desenvolvimento é removido para receber recursos para a próxima grande versão. O momento-chave para se ramificar de um novo ramo de lançamento do desenvolvimento é quando o desenvolvimento quase reflete o estado desejado da nova versão. Pelo menos, todos os recursos que são direcionados para versões de versão a serem construídas devem ser incorporados para se desenvolver neste momento. Todos os recursos visados em lançamentos futuros não podem - eles devem esperar até que o ramo de lançamento seja derivado. É exatamente no início de um ramo de lançamento que o próximo lançamento recebe um número de estratégia - não anteriormente. Os ramos de lançamento são criados a partir do ramo de desenvolvimento. Por exemplo, diga a versão 1. Então, nos ramificamos e damos ao ramo de lançamento um nome que reflete o novo número de versão :. Depois de criar um novo aplicativo de ramificação para ele, nós superamos o número da versão. Isso pode, naturalmente, ser uma mudança manual - o ponto é que alguns arquivos mudam. Então, o número da versão colidível está comprometido. Este novo ramo pode existir lá por um tempo, até que a liberação possa ser lançada definitivamente. Durante esse período, correções de erros podem ser aplicadas nesse ramo em vez de no ramo de desenvolvimento. A adição de novos recursos novos aqui é estritamente proibida. Eles devem ser fundidos em desenvolver e, portanto, aguardar o próximo grande lançamento. Quando o estado do ramo de lançamento está pronto para se tornar uma versão real, algumas ações precisam ser realizadas. Primeiro, o ramo de lançamento é mesclado em mestre, uma vez que cada commit no master é uma nova versão, por definição, lembrar. Em seguida, esse commit no master deve ser marcado para uma estratégia futura fácil para esta versão histórica. Finalmente, as mudanças feitas no ramo de lançamento precisam ser incorporadas de volta ao desenvolvimento de que os lançamentos futuros também contenham essas correções de bugs. O controle de versão mantém as mudanças feitas no ramo de lançamento, precisamos combinar esses de volta no desenvolvimento. Este passo pode levar a um conflito de mesclagem provavelmente mesmo, já que mudamos o número da versão. Se sim, conserte e cometer. Os ramos do Hotfix são muito parecidos com os ramos de lançamento, pois eles também devem se preparar para uma nova versão de produção, embora não planejada. Eles surgem controlando a necessidade de agir imediatamente após um estado não desejado de uma versão de produção ao vivo. Quando um erro crítico em uma versão de produção deve ser resolvido imediatamente, um ramo de hotfix pode ser derivado da tag correspondente na ramificação mestre que marca a versão de produção. A essência é que o trabalho dos membros da equipe no ramo de desenvolvimento pode continuar, enquanto outra pessoa está preparando uma solução de produção rápida. Mas as mudanças no desenvolvimento de aplicativos ainda são instáveis. Podemos então ramo de um ramo de hotfix e iniciar o problema do aplicativo. Isso é completamente semelhante ao modo como os ramos de liberação são finalizados. A fusão posterior do bugfix no ramo de lançamento acabará resultando em que o bugfix seja incorporado ao desenvolvimento também, quando o ramo de lançamento estiver concluído. Se o trabalho em desenvolvimento imediatamente requer esse bugfix e não aguarde até que o ramo de lançamento seja concluído, você pode combinar com segurança o bugfix para desenvolver agora já. Forma um elegante modelo de versão que é fácil de compreender e permite aos membros da equipe desenvolver uma compreensão compartilhada dos processos de ramificação e liberação. Uma versão em PDF de alta qualidade da figura é fornecida aqui. Vá em frente e coloque-o na parede para obter uma referência rápida a qualquer momento. E para qualquer um que o solicitou: Vincent Driessen é um engenheiro e consultor de software Python independente da Holanda. Você pode contratá-lo. Posts domésticos sobre um modelo bem sucedido da estratégia de ramificação. Por Vincent Driessen na terça-feira, 05 de janeiro
Estratégia de versão para uma API interna complexa.
3 pensamentos sobre & ldquo; Versão de versão do aplicativo & rdquo;
A fonte mais abrangente de cartas escritas pelos membros do Congresso Continental.
Freethinkers começa com o debate feroz sobre omitir Deus da constituição, e passa dos abolicionistas, sufragetes, direitos civis e movimentos feministas para as batalhas contemporâneas sobre a influência religiosa no governo.
Tem sido um mistério fascinante toda a minha vida através da família.
No comments:
Post a Comment