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.
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.
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 as alterações de código como uma narrativa , em vez de dividir as coisas por arquivos ou recursos.
Além de construir uma cultura de revisões de código mais eficazes, existem alguns outros benefícios de PRs empilhados:
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.
Os PRs empilhados suportam a prática shift-left porque as mudanças são continuamente integradas e testadas, o que permite a detecção precoce e retificação de problemas.
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!
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.
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
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, várias ferramentas 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.
À 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 demo/schema
, demo/backend
e demo/frontend
representam as 3 ramificações empilhadas umas sobre as outras.
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.
Mas isso não é tudo, mesclar um PR na pilha pode ser um verdadeiro pesadelo. Você tem 3 opções squash
, merge
e rebase
para mesclar um PR. Vamos tentar entender o que acontece nos bastidores de cada um.
squash
, 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.
Um merge
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 squash
, 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.
Embora essa abordagem dê mais contexto ao histórico de commits, ela cria inadvertidamente um git-history não linear que pode ser indesejável.
rebase
e merge, o Git irá reescrever os commits no branch base. Assim, semelhante à opção de commit squash
, ele perderá qualquer histórico associado aos commits originais.
Normalmente, se você estiver usando a estratégia merge
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 squash
ou um rebase
merge.
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 demo/schema
ramificação na linha principal. Ele criará um novo commit D1
que contém as alterações de A1
e A2
.
Como o Git não sabe de onde veio D1
e demo/backend
ainda é baseado no A2
, tentar rebasear demo/backend
no topo da linha principal criará conflitos de mesclagem.
Da mesma forma, rebasear demo/frontend
após rebasear demo/backend
também causará os mesmos problemas. Portanto, se você tivesse dez ramos empilhados e esmagasse um deles, teria que resolver esses conflitos nove vezes.
Ainda estamos apenas arranhando a superfície; existem muitos outros casos de uso , como reordenar commits, dividir, dobrar e renomear branches, que podem criar uma sobrecarga enorme para gerenciar ao lidar com PRs empilhados.
É por isso que construímos o gerenciamento de PRs empilhados como parte do Aviator.
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.
Criar uma pilha é bastante simples. Exceto neste caso, usamos av
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 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:
Usar a pilha agora se torna moleza. Você pode adicionar novos commits a qualquer ramificação e simplesmente executar av stack sync
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.
É 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 av pr queue
ou postando um comentário no GitHub: /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 av stack sync --trunk
para atualizar todos os PRs e limpar todos os PRs mesclados.
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 é de código aberto e totalmente gratuita. Adoraríamos que você experimentasse e compartilhasse seus comentários em nosso 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.