Como a Microsoft faz desenvolvimento com DevOps

A Microsoft busca usar o One Engineering System para criar e implantar todos os produtos da Microsoft com um forte processo de DevOps centrado em um fluxo de lançamento e branch do Git. Este artigo destaca a implementação prática, como o sistema é escalado desde pequenos serviços até 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 é uma tarefa ambiciosa. Os requisitos de diferentes organizações da Microsoft variam muito, e os requisitos de diferentes equipes dentro das organizações são escalados com tamanho e complexidade. Para atender a essas várias necessidades, a Microsoft usa uma estratégia de branch baseada em tronco para ajudar a desenvolver produtos com rapidez, implantá-los com regularidade e enviar alterações com segurança à produção.

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

Fluxo de lançamento da Microsoft

Todas as organizações devem estabelecer um processo de lançamento de código padrão para garantir a consistência entre as equipes. O fluxo de lançamento da Microsoft incorpora processos de DevOps desde o desenvolvimento até o lançamento. As etapas básicas do fluxo de lançamento consistem em branch, transmissão, solicitação de pull e mesclagem.

Branch

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

Push

Quando o desenvolvedor estiver pronto para integrar e enviar alterações ao restante da equipe, ele enviará o branch local para um branch no servidor e abrirá uma solicitação de pull. Repositórios com várias centenas de desenvolvedores trabalhando em muitos branches usam uma convenção de nomenclatura para branches de servidor a fim de evitar confusões e a proliferação de branches. Os desenvolvedores geralmente criam branches chamados users/<username>/feature, em que <username> é o nome da conta.

Solicitação de pull

O branch de tópico do controle de solicitações pull é mesclado no branch principal e garante que as políticas de branch sejam seguidas. O processo de solicitação de pull cria as alterações propostas e executa uma aprovação de teste rápido. Os conjuntos de testes de nível primário e secundário executam cerca de 60.000 testes em menos de cinco minutos. Essa não é a matriz de teste completa da Microsoft, mas é suficiente para oferecer confiança rapidamente às solicitações 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 é útil principalmente 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.

Mesclar

Depois que a solicitação de pull seguir todas as políticas de compilação e os revisores aprovarem, o branch de tópico será mesclado no branch de integração principal e a solicitação de pull será concluída.

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

Diferenças do GitHub Flow

O GitHub Flow é um popular fluxo de lançamento de desenvolvimento para que as organizações implementem uma abordagem escalável ao Git. No entanto, algumas organizações acreditam que, à medida que suas necessidades aumentam, elas devem divergir de partes do GitHub Flow.

Por exemplo, uma parte muitas vezes negligenciada do Flow GitHub é que as solicitações pull devem ser implantadas na produção para teste para que possam ser mescladas com o branch principal. Esse processo significa que todas as solicitações pull aguardam na fila de implantação para mesclagem.

Algumas equipes têm várias centenas de desenvolvedores trabalhando constantemente em um único repositório, que podem concluir mais de 200 solicitações pull para o branch principal por dia. Se cada solicitação de pull exigir uma implantação em vários datacenters do Azure em todo o mundo para teste, os desenvolvedores gastam tempo esperando que os branches sejam mesclados, em vez de criar softwares.

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

Detalhes de implementação

Veja a seguir alguns dos principais detalhes de implementação do fluxo de lançamento da Microsoft:

Estratégia de repositório Git

Diferentes equipes 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, principalmente componentes mais antigos, podem ter vários subcomponentes que têm subpastas diferentes dentro do componente pai.

Screenshot showing a Git repository structure.

Repositórios suplementares

Algumas equipes também gerenciam repositórios suplementares. Por exemplo agentes e tarefas de compilação e lançamento, a extensão do VS Code e projetos de código aberto são desenvolvidos no GitHub. Alterações de configuração são armazenadas em um repositório diferente. Outros pacotes dos quais a equipe depende vêm de outros lugares e são consumidos via NuGet.

Repositórios únicos ou múltiplos

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

Branches de lançamento

O fluxo de lançamento da Microsoft mantém o branch principal sempre compilável. Os desenvolvedores trabalham em branches de tópicos de curta duração que são mescladas em main. Quando uma equipe está pronta para o envio, seja no final de um sprint ou para uma grande atualização, ela inicia um novo branch de lançamento fora do principal. Os branches de lançamento nunca são mescladas no branch principal novamente, portanto, podem exigir alterações importantes de cherry-picking.

O diagrama a seguir mostra branches de curta duração em azul e branches de lançamento em preto. Um branch com um commit que precisa do recurso de cherry-picking é exibido em vermelho.

Diagram showing Git release branch structure.

Políticas e permissões de branch

As políticas de branch do Git ajudam a aplicar a estrutura de branch de lançamento e manter o branch principal limpo. Por exemplo, as políticas de branch podem impedir transmissões diretas para o branch principal.

Para manter a hierarquia de branch organizada, as equipes usam permissões para bloquear a criação de branches no nível raiz da hierarquia. No exemplo a seguir, todos podem criar branches em pastas como users/, features/ e teams/. Somente os gerentes de lançamento têm permissão para criar branches em releases/, e algumas ferramentas de automação têm permissão para a pasta integrations/.

Screenshot that shows branches.

Fluxo de trabalho do repositório Git

Dentro da estrutura do repositório e branch, os desenvolvedores fazem seu trabalho diário. Os ambientes de trabalho variam muito de acordo com a equipe e a pessoa. Alguns desenvolvedores preferem a linha de comando, outros gostam do Visual Studio, e outros trabalham em plataformas diferentes. 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

Criar um novo recurso é o núcleo do trabalho de um desenvolvedor de software. Partes que não são do Git do processo incluem examinar dados de telemetria, criar um esquema e uma especificação e escrever o código em si. Em seguida, o desenvolvedor começa a trabalhar com o repositório sincronizando com o commit mais recente em main. O branch principal é sempre compilável, por isso, com certeza é um bom ponto de partida. O desenvolvedor verifica um novo branch de recurso, faz alterações de código, confirma, transmite 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 uma solicitação de pull, os sistemas automatizados verificam se o novo código é compilado, 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 uma compilação bem-sucedida incluindo aprovação nos testes, aprovação dos proprietários e qualquer código alterado, várias verificações externas para verificar políticas corporativas antes que uma solicitação de pull possa ser concluída.

Screenshot showing the checks on a pull request.

Integrar com o 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 alterado são automaticamente adicionados como revisores. As equipes da Microsoft geralmente usam revisores opcionais para códigos que muitas pessoas alteram, como a geração de cliente REST e controles compartilhados, para que profissionais especializados analisem essas alterações.

Screenshot showing Teams integration.

Screenshot showing Teams notification of a pull request.

Implantar com sinalizadores de recursos

Depois que os revisores, os proprietários de código e a automação estiverem satisfeitos, 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 transmitir novamente as alterações. A automação é executada novamente no código fixo, mas uma pessoa não precisa aprovar novamente.

O branch é mesclado em main, e o novo código é implantado no próxima sprint ou lançamento principal. Isso não significa que o novo recurso será exibido imediatamente. A Microsoft dissocia 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 para estar pronto para ser exibido, é seguro acessar main se o produto for compilado e implantado. Uma vez em main, o código se torna parte de uma compilação oficial, onde é novamente testado, confirmado para atender à política e assinado digitalmente.

Shift left para detectar problemas com antecedência

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

Atualmente, um produto com mais de 200 solicitações pull pode produzir mais de 300 compilações de integração contínua por dia, totalizando mais de 500 execuções de teste a cada 24 horas. Esse nível de teste seria impossível sem o fluxo de trabalho de branch e lançamento baseado em tronco.

Lançamento em marcos de sprint

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

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

Illustration of the release branch at sprint 129.

Hotfixes de lançamento

Às vezes, as alterações precisam ser enviadas para produção rapidamente. A Microsoft geralmente não adiciona novos recursos no meio de um sprint, mas às vezes deseja lançar uma correção de bug rapidamente para desbloquear os usuários. Os problemas podem ser menores, como erros de digitação, ou grandes o suficiente para causar um problema de disponibilidade ou incidente no site ativo.

A correção desses problemas começa com o fluxo de trabalho normal. Um desenvolvedor cria um branch de main, tem o código revisado e conclui a solicitação de pull para mesclá-la. O processo sempre começa com fazer a alteração em main primeiro. Isso permite criar a correção com rapidez e validá-la localmente sem ter que alternar para o branch de lançamento.

Seguir esse processo também garante que a alteração entre em main, o que é fundamental. Corrigir um bug no branch de lançamento sem trazer a alteração de volta para main implicaria na repetição do bug durante a próxima implantação, quando o lançamento do sprint 130 se ramifica de main. É fácil esquecer de atualizar main durante a confusão e o estresse que podem surgir durante uma interrupção. Trazer mudanças para main primeiro faz com que as alterações sempre estejam tanto no branch principal quanto na de lançamento.

A funcionalidade do Git habilita esse fluxo de trabalho. Para enviar as alterações imediatamente à produção, depois que um desenvolvedor mesclar uma solicitação de pull em main, poderá usar a página de solicitação de pull para fazer cherry-picking das alterações no branch de lançamento. Esse processo cria uma nova solicitação de pull direcionada ao branch de lançamento, fazendo o backporting do conteúdo que acabou de ser mesclado em main.

Illustration of cherry-picking a hotfix commit into branch 129.

O uso da funcionalidade de cherry-picking abre uma solicitação de pull rapidamente, fornecendo a rastreabilidade e a confiabilidade das políticas de branch. O cherry-picking pode ocorrer no servidor, sem a necessidade de baixar o branch de lançamento para um computador local. Fazer alterações, corrigir conflitos de mesclagem ou fazer pequenas alterações devido a diferenças entre os dois branches são ações que podem ocorrer no servidor. As equipes podem editar alterações diretamente no editor de texto baseado no navegador ou por meio da Extensão de Conflito de Mesclagem de Solicitação de Pull para uma experiência mais avançada.

Depois que uma solicitação de pull tem como alvo o branch de lançamento, o código da equipe a revisa novamente, avalia as políticas de branch, testa a solicitação de pull e a mescla. Após a mesclagem, a correção é implantada no primeiro anel de servidores em minutos. A partir daí, a equipe implanta gradativamente a correção em mais contas usando anéis de implantação. À medida que as alterações são implantadas para mais usuários, a equipe monitora o sucesso e verifica se a alteração corrige o bug, sem introduzir deficiências ou lentidão. A correção eventualmente é implantada em todos os datacenters 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. Os membros criam o novo branch de lançamento, releases/M130 de main, e implantam esse branch.

Nesse momento, na verdade existem dois branches em produção. Com uma implantação baseada em anel para enviar alterações à produção com segurança, o anel rápido recebe as alterações do sprint 130 e os servidores de anel lentos permanecem no sprint 129 enquanto as novas alterações são validadas em produção.

Corrigir uma alteração no meio de uma implantação pode exigir o hotfix de dois lançamentos diferentes: do sprint 129 e do sprint 130. A equipe faz a portabilidade e implanta o hotfix em ambos os branches de lançamento. O branch 130 é implantado novamente com o hotfix para os anéis que já foram atualizados. O branch 129 é implantado novamente com o hotfix para os anéis externos que ainda não foram atualizados para o lançamento do próximo sprint.

Depois que todos os anéis são implantados, o antigo branch do sprint 129 é abandonado, porque qualquer alteração trazida para o branch do sprint 129 como um hotfix também foi feita em main. Portanto, essas alterações também estarão no branch releases/M130.

Illustration of a release branch at sprint 130.

Resumo

O modelo de fluxo de lançamento é fundamental na forma como a Microsoft desenvolve com DevOps para fornecer serviços online. Esse modelo usa uma estratégia de branch simples, baseada em tronco. Porém, em vez de manter os desenvolvedores presos em uma fila de implantação, esperando para mesclar suas alterações, o fluxo de lançamento da Microsoft permite que os desenvolvedores continuem trabalhando.

Esse modelo de lançamento também permite implantar novos recursos em datacenters do Azure em uma cadência regular, apesar do tamanho das bases de código da Microsoft e do número de desenvolvedores trabalhando nelas. O modelo também permite enviar os hotfixes à produção com rapidez e eficiência.