Compartilhar via


Como a Microsoft se desenvolve com o DevOps

A Microsoft se esforça para usar o One Engineering System para criar e implantar todos os produtos da Microsoft com um processo de DevOps sólido centrado em um fluxo de ramificação e lançamento do Git. Este artigo destaca a implementação prática, como o sistema é dimensionado de pequenos serviços para grandes necessidades de desenvolvimento de plataforma e lições aprendidas com o uso do sistema em várias equipes da Microsoft.

Adotar um processo de desenvolvimento padronizado é um empreendimento ambicioso. Os requisitos das diferentes organizações da Microsoft variam bastante, e os requisitos das diversas equipes dentro dessas organizações aumentam em escala com o tamanho e a complexidade. Para atender a essas necessidades variadas, a Microsoft usa uma estratégia de ramificação baseada em tronco para ajudar a desenvolver produtos rapidamente, implantá-los regularmente e fornecer alterações com segurança na produção.

A Microsoft também usa princípios de engenharia de plataforma como parte de seu One Engineering System.

Fluxo de lançamento da Microsoft

Cada organização deve resolver um processo de liberação de código padrão para garantir a consistência entre as equipes. O fluxo de lançamento da Microsoft incorpora processos de DevOps do desenvolvimento ao lançamento. As etapas básicas do fluxo de versão consistem em branch, push, solicitação de pull e mesclagem.

Ramo

Para corrigir um bug ou implementar um recurso, um desenvolvedor cria um novo branch fora do branch de integração principal. O modelo de ramificação leve do Git cria esses branches de tópico de curta duração para cada contribuição de código. Os desenvolvedores confirmam antecipadamente e evitam branches de recursos de longa execução usando sinalizadores de recursos.

Transmitir

Quando o desenvolvedor estiver pronto para integrar e entregar alterações para o restante da equipe, ele fará push da sua ramificação local para uma ramificação no servidor e abrirá uma solicitação de pull. Repositórios com várias centenas de desenvolvedores que trabalham em muitas ramificações usam uma convenção de nomenclatura para branches de servidor para aliviar a confusão e a proliferação de ramificações. Os desenvolvedores geralmente criam branches denominadas users/<username>/feature, onde <username> é o nome da conta.

Solicitação de pull

O branch do tópico de controle de solicitações de pull se mescla no branch principal e garante que as políticas de branch sejam atendidas. O processo de solicitação de pull cria as alterações propostas e executa um teste rápido. Os conjuntos de testes de primeiro e segundo nível executam cerca de 60.000 testes em menos de cinco minutos. Esta não é a matriz de teste completa da Microsoft, mas é suficiente para dar confiança rapidamente em solicitações de pull.

Em seguida, outros membros da equipe revisam o código e aprovam as alterações. A revisão de código continua de onde os testes automatizados pararam e é particularmente útil para detectar problemas de arquitetura. As revisões manuais de código garantem que outros engenheiros da equipe tenham visibilidade das alterações e que a qualidade do código permaneça alta.

Merge

Depois que o pull request satisfaz todas as políticas de build e os revisores o aprovam, o branch de tópicos se mescla ao branch de integração principal e o pull request é concluído.

Após a mesclagem, outros testes de aceitação são executados que levam mais tempo para serem concluídos. Esses testes pós-check-in tradicionais fazem uma validação mais completa. Esse processo de teste fornece um bom equilíbrio entre ter testes rápidos durante a revisão da solicitação de pull e ter uma cobertura de teste completa antes do lançamento.

Diferenças no Fluxo do GitHub

O GitHub Flow é um fluxo de versão de desenvolvimento popular baseado em tronco para que as organizações implementem uma abordagem escalonável para o Git. No entanto, algumas organizações acham que, à medida que suas necessidades crescem, elas devem divergir de partes do GitHub Flow.

Por exemplo, uma parte geralmente ignorada do GitHub Flow é que as pull requests devem ser implantadas na produção para realização de testes antes que possam integrar-se à branch principal. Esse processo significa que todos os pedidos de pull aguardam na fila de implantação para serem mesclados.

Algumas equipes têm várias centenas de desenvolvedores trabalhando constantemente em um único repositório, que podem concluir mais de 200 solicitações de pull no branch principal por dia. Se cada solicitação de pull exigir uma implantação em vários data centers do Azure em todo o mundo para teste, os desenvolvedores gastarão tempo aguardando a mesclagem de branches, em vez de escrever software.

Em vez disso, as equipes da Microsoft continuam desenvolvendo no branch principal e agrupam as implantações em versões cronometradas, geralmente alinhadas com uma cadência de sprint de três semanas.

Detalhes da implementação

Aqui estão alguns dos principais detalhes de implementação do fluxo de lançamento da Microsoft:

Estratégia do repositório Git

Equipes diferentes têm estratégias diferentes para gerenciar seus repositórios Git. Algumas equipes mantêm a maior parte de seu código em um repositório Git. O código é dividido em componentes, cada um em sua própria pasta de nível raiz. Componentes grandes, especialmente os mais antigos, podem ter vários subcomponentes com subpastas separadas dentro de seu componente principal.

Captura de tela mostrando uma estrutura do repositório Git.

Repositórios adjuntos

Algumas equipes também gerenciam repositórios adjuntos. Por exemplo, os agentes e tarefas de build e lançamento, a extensão do VS Code e os projetos de software livre são desenvolvidos no GitHub. As alterações de configuração são confirmadas em um repositório separado. Outros pacotes dos quais a equipe depende vêm de outros locais e são consumidos por meio do NuGet.

Repositório mono ou vários repositórios

Embora algumas equipes optem por ter um único repositório monolítico, o monopo, outros produtos da Microsoft usam uma abordagem de vários repositórios . O Skype, por exemplo, tem centenas de pequenos repositórios que se unem em várias combinações para criar muitos clientes, serviços e ferramentas diferentes. Especialmente para equipes que adotam microsserviços, múltiplos repositórios podem ser a abordagem certa. Normalmente, produtos mais antigos que começaram como monolitos acham que uma abordagem de mono-repo torna a transição para o Git mais fácil, e sua organização de código reflete isso.

Ramificações de versão

O fluxo de publicação da Microsoft mantém o branch principal construível em todos os momentos. Os desenvolvedores trabalham em ramos de tópicos de curta duração que são mesclados para main. Quando uma equipe estiver pronta para ser enviada, seja no final de um sprint ou para uma grande atualização, ela iniciará uma nova ramificação de lançamento fora do branch principal. Os ramos de lançamento nunca se mesclam de volta ao ramo principal, portanto, eles podem exigir seleção manual de alterações importantes.

O diagrama a seguir mostra ramificações de curto prazo em azul e ramificações de liberação em preto. Um branch com um commit que precisa de cherry-pick aparece em vermelho.

Diagrama mostrando a estrutura do branch de versão do Git.

Políticas e permissões de branch

As diretrizes de branch do Git ajudam a impor a estrutura do release branch e a manter o branch principal limpo. Por exemplo, as políticas de branch podem impedir pushes diretos para o ramo principal.

Para manter a hierarquia de ramificação organizada, as equipes usam permissões para bloquear a criação de ramificação no nível raiz da hierarquia. No exemplo a seguir, todos podem criar branches em pastas como usuários/, recursos/e equipes/. Somente os gerentes de release têm permissão para criar branches em releases/, e algumas ferramentas de automação têm permissão para a pasta integrações/.

Captura de tela que mostra ramificações.

Fluxo de trabalho do repositório Git

Dentro do repositório e da estrutura de branch, os desenvolvedores fazem seu trabalho diário. Os ambientes de trabalho variam muito por equipe e por indivíduo. Alguns desenvolvedores preferem a linha de comando, outros como o Visual Studio e outros trabalham em diferentes plataformas. As estruturas e políticas em vigor nos repositórios da Microsoft garantem uma base sólida e consistente.

Um fluxo de trabalho típico envolve as seguintes tarefas comuns:

Criar um novo recurso

A criação de um novo recurso é o núcleo do trabalho de um desenvolvedor de software. Partes não Git do processo incluem examinar dados de telemetria, criar um design e uma especificação e escrever o código real. Em seguida, o desenvolvedor começa a trabalhar com o repositório, sincronizando com o último commit em main. O branch principal é sempre construível, portanto, é sempre garantido como um bom ponto de partida. O desenvolvedor verifica um novo branch de recursos, faz alterações de código, confirma, envia por push para o servidor e inicia uma nova solicitação de pull.

Usar políticas e verificações de branch

Após a criação de um pull request, os sistemas automatizados verificam se o novo código compila, não quebra nada e não viola nenhuma política de segurança ou conformidade. Esse processo não impede que outros trabalhos ocorram em paralelo.

As políticas e verificações de branch podem exigir um build bem-sucedido, incluindo testes aprovados, aprovação dos responsáveis por qualquer código modificado e várias verificações externas para garantir conformidade com as políticas corporativas antes que um pull request possa ser concluído.

Captura de tela mostrando as verificações em uma solicitação de pull.

Integrar ao Microsoft Teams

Muitas equipes configuram a integração com o Microsoft Teams, que anuncia a nova solicitação de pull para os colegas de equipe dos desenvolvedores. Os proprietários de qualquer código tocado são adicionados automaticamente como revisores. As equipes da Microsoft geralmente usam revisores opcionais para código que muitas pessoas tocam, como a geração de clientes REST e controles compartilhados, para obter visão especializada dessas alterações.

Captura de tela mostrando a integração do Teams.

Captura de tela mostrando a notificação do Teams de uma solicitação de pull.

Implantar com sinalizadores de recursos

Depois que os revisores, os proprietários de código e a automação forem atendidos, o desenvolvedor poderá concluir a solicitação de pull. Se houver um conflito de mesclagem, o desenvolvedor receberá instruções sobre como sincronizar com o conflito, corrigi-lo e enviar novamente as alterações por push. A automação é executada novamente no código fixo, mas os humanos não precisam assinar novamente.

O branch se mescla maine o novo código é implantado na próxima versão principal ou sprint. Isso não significa que o novo recurso será exibido imediatamente. A Microsoft desassocia a implantação e a exposição de novos recursos usando sinalizadores de recursos.

Mesmo que o recurso precise de um pouco mais de trabalho antes de estar pronto para ser exibido, é seguro ir para main se o produto compilar e implantar. Uma vez dentro main, o código se torna parte de um build oficial, onde é novamente testado, confirmado para atender à política e assinado digitalmente.

Deslocar para a esquerda para detectar problemas mais cedo

Esse fluxo de trabalho do Git oferece vários benefícios. Primeiro, trabalhar em uma única ramificação principal praticamente elimina a dívida de mesclagem. Em segundo lugar, o fluxo de solicitação de pull fornece um ponto comum para impor testes, revisão de código e detecção de erros no início do pipeline. A estratégia de shift-left ajuda a reduzir o ciclo de feedback aos desenvolvedores, pois pode detectar erros em minutos, não em horas ou dias. Essa estratégia também fornece confiança para refatoração, pois todas as alterações são testadas constantemente.

Atualmente, um produto com mais de 200 solicitações de pull pode produzir mais de 300 builds de integração contínua por dia, o que equivale a mais de 500 execuções de teste a cada 24 horas. Sem o fluxo de trabalho de ramificação e liberação baseada em tronco, esse nível de teste seria impossível.

Lançamento em marcos de sprint

No final de cada sprint, a equipe cria uma ramificação de lançamento do branch principal. Por exemplo, no final do sprint 129, a equipe cria um branch de lançamento novo releases/M129. A equipe então coloca o branch sprint 129 em produção.

Após a ramificação do branch de lançamento, o branch principal permanece aberto para os desenvolvedores mesclarem alterações. Essas alterações serão implantadas três semanas depois na próxima implantação de sprint.

Ilustração do branch de 'release' no sprint 129.

Liberar hotfixes

Às vezes, as alterações precisam ir para a produção rapidamente. A Microsoft geralmente não adicionará novos recursos no meio de um sprint, mas às vezes deseja trazer uma correção de bug rapidamente para desbloquear os usuários. Problemas podem ser menores, como erros de digitação ou grandes o suficiente para causar um problema de disponibilidade ou incidente de site ao vivo.

A retificação desses problemas começa com o fluxo de trabalho normal. Um desenvolvedor cria um branch de main, em seguida, solicita a revisão do código e conclui o pull request para realizar a mesclagem. O processo sempre começa fazendo primeiro a alteração em main. Isso permite criar a correção rapidamente e validá-la localmente sem precisar alternar para o branch de versão.

Seguir esse processo também garante que a alteração seja incluída em main, o que é crítico. Corrigir um bug no ramo de release sem trazer a alteração de volta para main significaria que o bug se repetiria na próxima implantação, quando o ramo do sprint 130 for criado a partir de main. É fácil esquecer de atualizar main durante a confusão e o estresse que podem surgir durante uma interrupção. Colocar as alterações main em primeiro lugar significa sempre ter as alterações no branch principal e no branch de lançamento.

A funcionalidade do Git habilita esse fluxo de trabalho. Para colocar as alterações imediatamente em produção, uma vez que um desenvolvedor mescla uma solicitação mainde pull, ele pode usar a página de solicitação de pull para selecionar as alterações no branch de lançamento. Esse processo cria um novo pull request que tem como alvo o branch de lançamento, retroportando o conteúdo que foi recentemente mesclado em main.

Ilustração da escolha de uma confirmação de hotfix no branch 129.

O uso da funcionalidade de cherry-pick abre rapidamente um pull request, fornecendo a rastreabilidade e a confiabilidade das políticas de ramificação. Cherry-picking pode acontecer no servidor, sem precisar baixar a branch de versão para um computador local. Fazer alterações, corrigir conflitos de mesclagem ou fazer pequenas alterações devido a diferenças entre as duas ramificações podem ocorrer no servidor. As equipes podem modificar as alterações diretamente do editor de texto baseado em navegador ou por meio da Extensão de Conflito de Mesclagem de Pull Request para uma experiência mais avançada.

Depois que um pull request tem como alvo o branch de lançamento, a equipe realiza uma revisão de código novamente, avalia as políticas de branch, testa o pull request e mescla-o. Após a mesclagem, a correção é implantada no primeiro anel de servidores em minutos. A partir daí, a equipe implanta progressivamente a correção em mais contas usando anéis de implantação. À medida que as alterações são implantadas em mais usuários, a equipe monitora o êxito e verifica se a alteração corrige o bug enquanto não apresenta nenhuma deficiência ou lentidão. A correção eventualmente é implantada em todos os data centers do Azure.

Passar para o próximo sprint

Durante as próximas três semanas, a equipe termina de adicionar recursos ao sprint 130 e se prepara para implantar essas alterações. Eles criam o novo branch de lançamento, `releases/M130` a partir de `main`, e implantam esse branch.

Neste ponto, há, na verdade, duas ramificações em produção. Com uma implantação baseada em anel para trazer alterações à produção com segurança, o anel rápido obtém as alterações do sprint 130 e os servidores de anel lentos permanecem no sprint 129 enquanto as novas alterações são validadas na produção.

Realizar uma correção rápida durante uma implantação pode exigir a correção rápida de duas versões diferentes, a versão da sprint 129 e a versão da sprint 130. A equipe porta e implanta o hotfix em ambas as ramificações de lançamento. O branch 130 é reimplantado junto com o hotfix nos anéis que já foram atualizados. O branch 129 é reimplantado com o hotfix nos anéis externos que ainda não foram atualizados para a versão da próxima sprint.

Depois que todos os anéis são implantados, o antigo ramo sprint 129 é abandonado, porque todas as alterações trazidas para o ramo sprint 129 como um hotfix também foram feitas em main. Portanto, essas alterações também estarão no releases/M130 branch.

Ilustração de um ramo de liberação no sprint 130.

Resumo

O modelo de fluxo de lançamento está no centro de como a Microsoft se desenvolve com o DevOps para fornecer serviços online. Esse modelo usa uma estratégia simples de ramificação baseada em tronco. Mas, em vez de manter os desenvolvedores presos em uma fila de implantação, aguardando para mesclar suas alterações, o fluxo de lançamento da Microsoft permite que os desenvolvedores continuem trabalhando.

Esse modelo de versão também permite implantar novos recursos em data centers do Azure em uma cadência regular, apesar do tamanho das bases de código da Microsoft e do número de desenvolvedores trabalhando neles. O modelo também permite colocar correções rápidas em produção de forma rápida e eficiente.