Nos dias de hoje, devemos pensar criticamente sobre como fazemos as coisas na programação. Precisamos aplicar a abordagem de engenharia aos nossos processos. Nós, os engenheiros de software, confiamos nas discussões sobre classes abstratas e funções puras. Por outro lado, fugimos quando há necessidade de discutir coisas "gerais". A revisão do código tem algumas falhas e temos que redesenhá-la corrigindo a estrutura do processo e a divergência de visão.
Companies Mentioned
Nos dias de hoje, devemos pensar criticamente sobre como fazemos as coisas na programação. Precisamos aplicar a abordagem de engenharia aos nossos processos. Nós, os engenheiros de software, confiamos nas discussões sobre classes abstratas e funções puras. Por outro lado, fugimos quando há necessidade de discutir coisas "gerais". Meu processo de programação favorito amplamente difundido e com uma enorme quantidade de falhas é a revisão de código. Esta história irá analisá-lo de diferentes perspectivas e propor melhorias. O primeiro fato significativo que li sobre inspeções de software foi no livro "Facts and Fallacies of Software Engineering" de Robert Glass. Ele afirma o seguinte:
Inspeções rigorosas podem remover até 90% dos erros de um produto de software antes que o primeiro caso de teste seja executado.
Não consegui determinar se essas palavras eram apenas sobre a revisão do código. Trato-os como referidos a diferentes tipos de inspeções, que descreverei mais adiante.
. Michael Fagan formulou a ideia de inspeções em 1976 em seu artigo "Inspeções de design e código para reduzir erros no desenvolvimento de programas".
Eu descobri três tipos de inspeções a seguir lá:
inspeção de projeto,
Inspeção de código antes do teste de unidade,
Inspeção de código após o teste de unidade.
O trabalho de Fagan não propõe uma nova abordagem ao código, mas documenta os fenômenos já existentes e os defende. No entanto, o artigo é o sinal escrito mais antigo de inspeções que encontrei. As inspeções de código se parecem com nossas revisões de código modernas. Por que sentimos falta de outros tipos de inspeções hoje?
Por que existem apenas revisões de código hoje: algumas suposições
A popularidade das inspeções de código e a quase inexistência de outros tipos de inspeções hoje vem de nossas ferramentas. GitHub, BitBucket ou GitLab têm instrumentos de revisão de código integrados e se encaixam naturalmente no fluxo do Git, no fluxo do GitHub e em outras abordagens. Que ferramenta você usa para atividades de design? Não é sobre a interface do usuário/UX. É sobre a estrutura do código que você irá construir. Você pode ter ouvido falar de ferramentas CASE ou UML. Não os vi seriamente usados em nenhuma empresa para a qual trabalhei, e já trabalhei por sete.
No HackerNoon, a consulta de pesquisa " UML " resulta em apenas dois resultados relevantes. Portanto, não há lugar para introduzir a inspeção de projeto quando não há um processo de projeto de solução tangível. Na equipe que lidero, usamos o Miro para design de interface. O processo poderia ter sido mais satisfatório: como em outras ferramentas de diagramação, você logo começa a desenhar em vez de resolver seus problemas. Podemos ser independentes de nossas ferramentas. Aqui está uma pequena citação para apoiar isso do livro "Investments Unlimited":
...se apenas fizermos o que as ferramentas podem fazer - então sempre estaremos limitados às capacidades de nossas ferramentas.
Quais falhas as revisões de código existentes têm?
Vejamos o processo em sua forma clássica de diferentes perspectivas. Cada um deles apresenta problemas significativos.
Perspectiva BPMN
BPMN é uma notação de modelagem de processos de negócios. Descreve processos com ações, eventos, gateways lógicos, mensagens e outros meios. Eu até recomendo usá-lo se você quiser desenvolver um algoritmo, pois é mais descritivo do que um fluxograma. Vamos descrever o processo de revisão de código para um único revisor com esta notação e analisá-lo. Eu usei uma ferramenta baseada em texto para gerar . Há uma pequena falha com muitas cartas de retorno.
Tudo começa com uma criação de relações públicas e nada é notável aqui. O próximo passo é notificar um revisor, que é uma simplificação de todos os diferentes meios disponíveis para dizer: "Ei, meu PR está esperando por você!👋" O importante aqui é a liberação das atividades atuais. Aqui o PR espera por uma duração desconhecida. Em seguida, o revisor mergulha na tarefa e conduz a revisão. Há uma chance de que um PR seja mesclado imediatamente. No entanto, poderia acontecer o contrário: apareceriam alguns comentários que precisavam de correções. O autor do código pode já estar na próxima tarefa e há mais um tempo de espera de duração desconhecida. Além disso, voltar requer a restauração do contexto, a interpretação dos comentários e sua correção. O próximo passo é a notificação do revisor.
Já não estivemos lá? Sim você está certo. Acabamos de terminar nossa primeira iteração do loop potencialmente infinito. Sim, infinito. Há sempre uma chance de que novos comentários apareçam. Ou que um dos períodos de espera duraria para sempre.
Queremos o loop potencialmente infinito como parte de nossas operações diárias? Não tenho certeza, pois geralmente é preferível uma entrega mais rápida.
Perspectiva da Visão da Solução
Às vezes, temos desenvolvedores ou arquitetos seniores em nossas equipes como revisores. Eles desejam ter uma base de código consistente, aderir a alguns métodos e padrões e evitar outros. Eles têm uma visão. Os desenvolvedores também têm sua ideia. Normalmente, eles não estão cientes da intenção de seus superiores. Requer transmissão consistente ou um ambiente auxiliar, o que raramente acontece. Vamos dar uma olhada na imagem a seguir.
Você pode ver como as duas visões dos participantes da revisão de código diferem. Após a primeira iteração, eles iniciam o alinhamento, mas ainda há um caminho a percorrer. O revisor ajusta a visão e o autor do código se move de acordo com a interpretação das propostas. Podemos usar a metáfora "imagine que você pediu uma casa e surpreenda! Não é a que você esperava". Ou podemos olhar para o seu núcleo. Imagine que você pediu a uma pessoa para conseguir algo. Então você volta e vê os resultados, mas fica surpreso com o conjunto de decisões que o empreendedor tomou. Não se surpreenda, pois você não exigiu uma estrutura específica de tomada de decisão.
Perspectiva Interpessoal
A imagem diz por si. Você pediria a seu colega engenheiro para corrigir o problema de projeto depois de passar muitos dias na tarefa? Imagine que seu sprint acabou e o ticket foi a causa de alguma tensão em uma trocação. Você ajudará seu colega a mesclá-lo o mais rápido possível. Por outro lado, pode haver um engenheiro sênior revisando o código do júnior. Ele poderia pedir que ele percorresse um longo caminho para consertar a decisão tomada no início. No entanto, este é o momento certo para dar tal conselho? Tantas decisões já estão na escolha errada.
A manufatura enxuta ainda não influenciou a programação. No entanto, já temos uma ferramenta do livro "Lean Software Development" de Mary Poppendieck e Tom Poppendieck. Esta ferramenta são os sete desperdícios de desenvolvimento de software:
Trabalho parcialmente feito,
Recursos extras,
Reaprendendo,
Handoffs,
atrasos,
Troca de tarefas,
Defeitos.
Revisão de código clássico ganha 6 de 7!🎉
Trabalho parcialmente feito . Uma tarefa na revisão é abandonada na maioria das vezes. Costumo tratar esse estágio de desenvolvimento como uma fila, em vez de uma onde está a ação. Criar uma revisão também tem um efeito psicológico interessante: "A tarefa está pronta e não é mais meu trabalho!" A revisão de código é a "terra de responsabilidade de ninguém", e é por isso que muitas vezes a vemos escalar para os níveis superiores.
Reaprendendo . Você pode ver a reaprendizagem no diagrama BPMN acima. Restaurar o contexto é reaprender.
Entregas . Se formos honestos, isso não é um desperdício aqui. É a mecânica central.
Atrasos . O design de revisão de código contém dois tipos de atrasos que discutimos anteriormente. O que é ainda mais assustador é o loop deles.
Troca de tarefas . As pessoas pausam suas tarefas para dar atenção a uma revisão.
Defeitos . A revisão é boa para encontrar problemas cosméticos, mas não falhas de design, o que prejudicaria mais. A mencionada falta de motivação para pedir aos bolsistas mudanças significativas leva a defeitos substanciais no projeto.
Cobrimos os problemas de revisões de código de vários lados. O que podemos fazer para corrigir esse processo?
Revisão de Código de Redesenho
Nesta seção, abordaremos os mesmos tópicos da seção anterior, mas do ponto de vista da fixação.
Consertando a estrutura do processo
Aqui temos um autor de código aguardando a conclusão de uma revisão e uma sessão de programação em pares após a visão geral de um revisor.
No processo proposto, podemos observar várias mudanças significativas em relação ao anterior:
Não há mais loop de revisão potencialmente infinito;
Tempo de espera único de duração desconhecida, também cuidaremos disso;
Não há necessidade de restaurar o contexto ou interpretar o feedback para o autor do código. Os comentários agora servem como lembretes para o par.
Quais são os principais pré-requisitos para que esse processo aconteça? Agora, uma equipe precisa de uma função adicional. Isso implica que alguém faz as atividades auxiliares, por exemplo, lida com a dívida técnica ou corrige bugs de menor prioridade. Quando a revisão de código aparece no radar, essa pessoa abandona imediatamente a tarefa atual e pula para o PR que chegou. Se você já se perguntou por que precisa de alguma folga na estrutura do seu trabalho, este é um exemplo. Se todos estiverem ocupados com os recursos de alta prioridade, você os entregará mais tarde.
Corrigindo a divergência de visão
O que discutimos nas revisões de código? As decisões tomadas durante o desenvolvimento. Fizemos alguns deles há uma hora e outros há uma semana. Nossas escolhas se complementam e não são independentes.
Quão agradável você acha uma oportunidade de questionar uma das primeiras decisões sobre as quais as próximas centenas se posicionam? Você pode estar insatisfeito com a oportunidade. O momento mais apropriado para discutir as decisões de design é quando elas aparecem. Precisamos de um tipo adicional de revisão: revisão de design. Às vezes, quando o problema é desafiador, é bom dedicar algum tempo ao plano e discuti-lo com seus colegas experientes. Há um famoso ditado militar:
Nenhum plano de batalha sobrevive ao contato com o inimigo.
Se traduzirmos para a linguagem de sistemas, obteremos algo como: "Sistema certamente precisará ajustar seu comportamento quando chegar o primeiro feedback". No caso da programação, o feedback seria os problemas que enfrentamos ao tentar implementar o design em nosso sistema. Algumas das decisões precisam ser revistas. Precisaríamos alterá-los e novamente divergir em nossas visões com um revisor. Adam Thornhill, em seu impressionante livro "Software Design X-Rays", propôs um método:
É por isso que recomendo que você faça o passo a passo inicial do código muito antes. Em vez de esperar pela conclusão de um recurso, torne uma prática apresentar e discutir cada implementação em um terço da conclusão. Concentre-se menos nos detalhes e mais na estrutura geral, nas dependências e em como o design se alinha com o domínio do problema. É claro que a conclusão de um terço é subjetiva, mas deve ser um ponto em que a estrutura básica esteja instalada, o problema seja bem compreendido e o conjunto de testes inicial exista. Nesta fase inicial, um retrabalho do projeto ainda é uma alternativa viável e detectar problemas potenciais aqui tem uma grande recompensa.
Eu criei um termo para minha equipe ter uma linguagem conveniente: revisão de esqueleto. Espero que ajude a refletir a ideia por trás da atividade.
Perspectiva Lean sobre o Processo Proposto
O processo proposto elimina ou aborda significativamente os resíduos descobertos.
Trabalho parcialmente feito . Não é permitido alternar para outra tarefa durante o tempo de antecipação de um autor de código, portanto, não há trabalho parcialmente concluído significativo para o usuário. Atividades parcialmente realizadas de menor prioridade são o trade-off.
Reaprendendo . Nenhum reaprendizado ocorre, pois pouco tempo passa entre a conclusão da codificação e a programação em par.
Atrasos . Encurtamos significativamente o atraso do revisor de código e eliminamos o do autor.
Troca de tarefas . Não é mais permitido para um autor e gerenciado para um revisor.
Defeitos . Agora consertar defeitos de projeto fica mais barato, e o mais importante deles, os defeitos de projeto, são corrigidos.
Pensamentos Adicionais
Analisamos a abordagem de revisão de código para um único autor e um único revisor. Quando mais revisores aparecem, os problemas se multiplicam. Então, infelizmente, nem todos os bugs se tornam superficiais se você adicionar pessoas recentemente para ver todas as decisões que você tomou. Em vez disso, você faria seu código mesclar mais tarde. Dois dos problemas mais desafiadores que enfrentei ao tentar introduzir o processo proposto foram os seguintes:
Os desenvolvedores tratam o estágio de revisão como um trabalho concluído. Os gerentes ficam horrorizados quando propõem a introdução de redundância no trabalho diário. É ótimo que eles não gerenciem as equipes de bombeiros. A cura para o primeiro problema é a repetição e a velocidade.
O segundo problema é mais complicado. Aqui eu quero citar o livro "Actionable Agile Metrics for Predictability" de Daniel Vacanti:
Existem muitas estratégias que a FedEx emprega, mas a que provavelmente é mais importante é que a qualquer momento a FedEx tem aviões vazios no ar. Sim, eu disse aviões vazios. Dessa forma, se um local ficar sobrecarregado ou se os pacotes forem deixados para trás porque um avião programado regularmente estava cheio, um avião vazio será redirecionado (just-in-time, deve-se dizer) para o local do problema. A qualquer momento, a FedEx tem "sobressalentes no ar"!
Se você for um gerente, considere isso na próxima vez que planejar a maximização da utilização. Estamos satisfeitos com a atualização? Sim, parece melhor do que o que temos agora.
Podemos fazer melhor? Sim, nós podemos.
A meta é eliminar a revisão de código no momento em que podemos garantir que toda a qualidade necessária já está no código. Para conseguir isso, precisamos construir uma estrutura auxiliar de tomada de decisão para ajudar os desenvolvedores a aplicar o que é tratado como uma boa prática e evitar a má. Nunca ouvi falar de tal estrutura, mas os linters in-IDE são um passo em direção a isso.
Obrigado por ler! Escreva um comentário se quiser discutir as ideias descritas.