Repensando as revisões de código com PRs empilhados Este post explora os princípios shift-left e sugere que os PRs empilhados se tornarão cada vez mais úteis. O processo de revisão de código por pares é uma parte essencial do desenvolvimento de software. Ele ajuda a manter a qualidade do software e promove a adesão a padrões, requisitos de projeto, guias de estilo e facilita o aprendizado e a transferência de conhecimento. Eficácia da Revisão de Código Embora a eficácia seja alta para revisar alterações de código suficientemente pequenas, ela cai exponencialmente com o aumento do tamanho da alteração. Para manter o nível necessário de foco mental para ser eficaz, grandes revisões de código são exaustivas. Normalmente, quanto maior a duração da revisão, menos eficaz a revisão geral se torna: Então, por que não podemos simplesmente restringir o tamanho das solicitações pull (PRs)? Embora muitas alterações possam começar pequenas, de repente uma pequena alteração de duas linhas pode se transformar em uma refatoração de 500 linhas, incluindo várias conversas de ida e volta com os revisores. Algumas equipes de engenharia também mantêm ramificações de recursos de longa duração enquanto continuam trabalhando, dificultando a revisão. Então, como podemos encontrar o equilíbrio certo? Simples. Use PRs empilhados. O que são PRs empilhados? As solicitações pull empilhadas fazem alterações iterativas menores e são empilhadas umas sobre as outras, em vez de agrupar grandes alterações monolíticas em uma única solicitação pull. Cada PR na pilha se concentra em apenas uma alteração lógica, tornando o processo de revisão mais gerenciável e menos demorado. Também escrevemos uma postagem no ano passado explicando como essa ajuda representa , em vez de dividir as coisas por arquivos ou recursos. as alterações de código como uma narrativa Por que PRs empilhados? Além de construir uma cultura de revisões de código mais eficazes, existem alguns outros benefícios de PRs empilhados: Feedback inicial da revisão do código Imagine que você está implementando um grande recurso. Em vez de criar todo o recurso e, em seguida, solicitar uma revisão de código, considere esculpir a estrutura inicial e enviá-la imediatamente para feedback. Isso pode economizar inúmeras horas ao obter feedback antecipado sobre seu design. Ciclo de feedback CI mais rápido Os PRs empilhados suportam a prática porque as mudanças são continuamente integradas e testadas, o que permite a detecção precoce e retificação de problemas. shift-left As alterações são mescladas em pedaços, detectando quaisquer problemas no início, em vez de mesclar uma mudança gigante, esperando que não derrube o prod! Compartilhamento de conhecimento As revisões de código também são maravilhosas para a posteridade. Suas alterações de código estão narrando seu processo de pensamento por trás da implementação de um recurso, portanto, o detalhamento das alterações cria uma transferência de conhecimento mais eficaz. É mais fácil para os membros da equipe entender as mudanças, o que promove um melhor compartilhamento de conhecimento para o futuro. Permanecendo Desbloqueado Esperar para ter o código revisado e aprovado pode ser um processo frustrante. Com PRs empilhados, os desenvolvedores podem trabalhar em várias partes de um recurso sem esperar que os revisores aprovem PRs anteriores Qual é o problema? Então, por que mais desenvolvedores não usam PRs empilhados para revisões de código? Embora esse fluxo de trabalho de RP empilhado aborde as práticas desejadas de manter as revisões de código gerenciáveis e os desenvolvedores produtivos, infelizmente, ele não é muito bem suportado nativamente pelo Git ou pelo GitHub. Como resultado, foram desenvolvidas na comunidade de código aberto para permitir que os engenheiros incorporem essa técnica de empilhamento nas plataformas Git e GitHub existentes. Mas empilhar os PRs é apenas parte da história. várias ferramentas Atualizando À medida que obtemos feedback de revisão de código e fazemos alterações na parte da pilha, agora temos que rebasear e resolver conflitos em todas as ramificações subsequentes. Vamos dar um exemplo. Imagine que você está trabalhando em uma mudança que requer uma mudança de esquema, uma mudança de back-end e uma mudança de front-end. Com isso, agora você pode enviar uma alteração de esquema simples para revisão primeiro e, enquanto isso está sendo revisado, você pode começar a trabalhar no back-end e no front-end. Usando PRs empilhados, todas essas 3 alterações podem ser revisadas por 3 revisões diferentes. Nesse caso, você pode ter uma pilha parecida com esta, onde , e representam as 3 ramificações empilhadas umas sobre as outras. demo/schema demo/backend demo/frontend Até agora, isso faz sentido, mas e se você receber alguns comentários de revisão de código sobre a mudança de esquema que requer a criação de um novo commit? De repente, seu histórico de commits fica assim: Agora, você precisa rebasear manualmente todas as ramificações subsequentes e resolver conflitos em cada estágio. Imagine se você tiver 10 ramos empilhados onde pode ter que resolver os conflitos 10 vezes. Mesclando Mas isso não é tudo, mesclar um PR na pilha pode ser um verdadeiro pesadelo. Você tem 3 opções , e para mesclar um PR. Vamos tentar entender o que acontece nos bastidores de cada um. squash merge rebase No caso de um commit , o Git pega as alterações de todos os commits existentes do PR e os reescreve em um único commit. Nesse caso, nenhum histórico é mantido sobre a origem dessas alterações. squash Um commit é um tipo especial de Git commit que é representado por uma combinação de dois ou mais commits. Portanto, funciona de maneira muito semelhante a um commit , mas também captura informações sobre seus pais. Em um cenário típico, uma confirmação de mesclagem tem dois pais: a última confirmação na ramificação base (onde o PR é mesclado) e a confirmação superior na ramificação do recurso que foi mesclada. merge squash Embora essa abordagem dê mais contexto ao histórico de commits, ela cria inadvertidamente um que pode ser indesejável. git-history não linear Por fim, no caso de e merge, o Git irá reescrever os commits no branch base. Assim, semelhante à opção de commit , ele perderá qualquer histórico associado aos commits originais. rebase squash Normalmente, se você estiver usando a estratégia commit ao empilhar PRs, sua vida será um pouco mais simples, mas a maioria das equipes desencoraja o uso dessa estratégia para manter o git-history limpo. Isso significa que você provavelmente está usando um ou um merge. merge squash rebase E isso cria um conflito de mesclagem para todas as ramificações empilhadas não mescladas subsequentes. No exemplo acima, digamos que esmagamos a primeira ramificação na linha principal. Ele criará um novo commit que contém as alterações de e . demo/schema D1 A1 A2 Como o Git não sabe de onde veio e ainda é baseado no , tentar rebasear no topo da linha principal criará conflitos de mesclagem. D1 demo/backend A2 demo/backend Da mesma forma, rebasear após rebasear também causará os mesmos problemas. Portanto, se você tivesse dez ramos empilhados e esmagasse um deles, teria que resolver esses conflitos nove vezes. demo/frontend demo/backend Ainda estamos apenas arranhando a superfície; existem , como reordenar commits, dividir, dobrar e renomear branches, que podem criar uma sobrecarga enorme para gerenciar ao lidar com PRs empilhados. muitos outros casos de uso É por isso que construímos o gerenciamento de PRs empilhados como parte do Aviator. Por que o Aviator CLI é diferente Pense no Aviator como uma camada de aumento que fica em cima de suas ferramentas existentes. O Aviator se conecta ao GitHub, Slack, Chrome e Git CLI para fornecer uma experiência de desenvolvedor aprimorada. O Aviator CLI funciona perfeitamente com todo o resto! A CLI não é apenas uma camada sobre o Git, mas também entende o contexto das pilhas no GitHub. Vamos considerar um exemplo. Criando uma pilha Criar uma pilha é bastante simples. Exceto neste caso, usamos CLI para criar as ramificações para garantir que a pilha seja rastreada. Por exemplo, para criar sua ramificação de esquema e PR correspondente, siga as etapas abaixo. av av stack branch demo/schema # make schema changes git commit -a -m "[demo] schema changes" av pr create Como o Aviator também está conectado ao seu GitHub, facilita a visualização da pilha. Ou se você quiser visualizá-lo no terminal, ainda pode fazer isso com os comandos da CLI: Atualizando a Pilha Usar a pilha agora se torna moleza. Você pode adicionar novos commits a qualquer ramificação e simplesmente executar de qualquer lugar na pilha para sincronizar todas as ramificações. O Aviator rebase automaticamente todas as ramificações para você e, se houver um conflito de mesclagem real, basta resolvê-lo uma vez. av stack sync Mesclando a Pilha É aqui que as ferramentas Aviator se destacam facilmente de qualquer ferramenta existente. Na Aviator, construímos um dos MergeQueue mais avançados para gerenciar a mesclagem automática de milhares de alterações em escala. O Aviator suporta integração perfeita com CLI e PRs empilhados. Portanto, para mesclar a pilha parcial ou completa de PRs, você pode atribuí-los ao Aviator MergeQueue usando CLI ou postando um comentário no GitHub: . av pr queue /aviator stack merge O Aviator lida automaticamente com a validação, atualização e mesclagem automática de todas as pilhas enfileiradas em ordem. Agora, quando os PRs forem mesclados, desta vez você pode executar para atualizar todos os PRs e limpar todos os PRs mesclados. av stack sync --trunk Shift-Esquerda é o futuro Inicialmente, os PRs empilhados podem parecer mais trabalhosos devido à necessidade de dividir as alterações em partes menores. No entanto, o aumento na eficiência da revisão de código, os ciclos de feedback mais rápidos e as oportunidades de aprendizado aprimoradas certamente superarão essa sobrecarga. À medida que continuamos adotando os princípios shift-left, os PRs empilhados se tornarão cada vez mais úteis. O Aviator CLI oferece uma ótima maneira de gerenciar PRs empilhados com muito menos tédio. A CLI é e totalmente gratuita. Adoraríamos que você experimentasse e compartilhasse seus comentários em nosso . de código aberto fórum de discussão Na Aviator, estamos criando ferramentas de produtividade para desenvolvedores a partir dos primeiros princípios para capacitar os desenvolvedores a criar de forma mais rápida e melhor.