Revisão de Código
Fazer código é muito simples. Abra o seu editor de texto, escreva umas linhas de código, talvez alguns testes unitários e pronto, você finalizou o seu trabalho, certo? O problema vem logo após isso, a maior barreira de todo programador, revisão de código e por fim os bugs.
Quem nunca começou o dia abrindo o email e lendo uma série de notificações de pull requests para revisar ou notificações de novos eventos de erro que apareceram no Sentry (ou outra ferramenta para tracking de erro). Em algum ponto específico da sua carreira você vai se perguntar, “o que eu fiz para merecer isso”?
Vou dar um exemplo de uma pull request horrível. Um monte de tarefas feitas de uma única vez, sem contexto, várias linhas de código e uma branch sem nome (somente o id da task no Jira). Algumas vezes pull requests similares são escritas sem nenhum teste unitário. De cara eu sei que essa pull request vai levar horas para uma boa revisão. O pior acontece quando foi eu quem fez essa porcaria.
Um bug sempre é um problema do processo de revisão de código
Antes e tudo, trate qualquer runtime exception ou comportamento indesejado como um bug. Reconheça que bugs introduzidos na aplicação são resultados de um processo de revisão de código fraco e deve ser revisto. Revisar o processo de revisão de código é uma tarefa realmente difícil mas não impossível. Para melhorar o processo de revisão de código na Arquivei tomamos as seguintes iniciativas:
- Encorajamos o time a fazer uma pré revisão de código. Pré revisão de código é apenas revisar o próprio código antes de criar uma pull request. O Atom e o VSCode são dois editores que nos ajudam nisso, basta abrir a seção do git e revisar arquivo por arquivo, linha por linha. Além disso, encorajamos os revisores a testar a pull request no processo de revisão;
- Envolvemos dois revisores em cada pull request. Isso é realmente comum e garante mais confiabilidade no nosso código, em alguns casos até livre de bugs. Acredite, isso não é tão doloroso quanto parece e várias empresas mantêm mais de um revisor no processo;
- Escrevemos menos linhas de código. Pull requests menores, concisas e com contexto promovem menos preguiça nos desenvolvedores, e mais importante do que apenas o código é ter uma excelente mensagem de commit descrevendo o contexto, o objetivo das modificações e como testá-la. Pretendo escrever um artigo sobre mensagens de commit no futuro;
- Escrevemos testes unitários para cada modificação dando uma boa cobertura para o nosso código (o mais próximo possível de 100%). Essa é uma tarefa cansativa e realmente difícil, mas nunca impossível. Meu próximo passo é conseguir introduzir o mindset TDD na equipe para evitar a ausência de teste unitário nos commits;
- Criamos um check list para a revisão da pull request. Isso soa realmente como estranho em um primeiro momento mas pode ser interessante para o seu contexto. Eu tentei usar check list no processo de revisão de código algumas vezes no time de frontend da Arquivei e falhei. Como eu disse, isso pode ser interessante no seu contexto, não foi o nosso caso;
- E, finalmente, a parte mais importante. Definimos o impacto do nosso código!
Impacto
Nas duas primeiras imagens deste artigo eu mostrei como fazer um pull request ruim, um processo de code review péssimo e usar o git da maneira mais errada possível. Que tal agora mostrarmos o jeito certo.
Para esclarecer como fazer isso vamos assumir que fizemos uma modificação em uma função que modifica o comportamento da uma classe, que modifica o comportamento de um pacote, que modifica o comportamento da aplicação, que modifica o comportamento da empresa, que modifica o comportamento do usuário. Agora vamos assumir que escrevemos o nome de uma branch ruim, um commit deplorável e por fim uma pull request “merda” (como a minha) e algum desenvolvedor, por razões óbvias, revisou de forma preguiçosa e, por fim, introduzimos um bug.
Em um primeiro momento a nossa simples modificação dentro da função parecia simples e inofensiva mas promoveu um impacto muito grande na empresa. Vamos começar a prever o impacto analisando a onda de impacto.
Bom, agora que a onda de impacto foi definida já é possível mensurarmos o impacto de uma modificação feita. Vamos definir a onda de impacto pela força da sua modificação.
Impacto Limitado
O impacto limitado é previsível e tem um contexto muito restrito. Essas modificações não alteram a regra de negócio da empresa.
- Trivial: modificações simples como mudança de texto, modificar o processo do pipeline (CI/CD), renomear variáveis, renomear funções ou toda e qualquer modificação que não afete o usuário, que seja realmente simples de fazer e que os outros desenvolvedores vão entender que o impacto é limitado e muito trivial;
- Refactor: reescrever um trecho de código, função, classe ou pacote sem modificar o seu comportamento e a sua responsabilidade. Nesta zona de impacto o código deve ter testes unitários.
Impacto Ilimitado
O impacto ilimitado pode ser imprevisível. Com isso, você deve atuar nesse ambiente de forma a amortizar a dor do seu usuário. O primeiro passo é definir nichos de usuários que serão impactados através de “rollouts”. Recomendo estudar o uso de “feature flags” para lançar o seu produto em diferentes fases. Na Arquivei nós usamos o Split para fazer gestão de feature flag.
- Feature: Uma funcionalidade nova que será adicionada na sua plataforma. Geralmente uma funcionalidade nova não causa estresse no usuário pois não modifica as funcionalidades que ele já usa. Neste cenário você consegue administrar a ansiedade do usuário através da curva de adoção do produto e usando feature flags;
- Bugfix: Uma modificação que ajuste um “runtime exception” ou um comportamento indesejado na sua aplicação. Considere fazer isso com o menor número de modificações possível e o mais breve possível, refatore o código que envolve o bug e crie casos de testes para esse caso particular;
- Modification: Aqui entram os impactos quase que imprevisíveis. Uma modificação que muda a sua regra de negócio. Por mais que tenha testes unitários e não promovam bugs esse tipo de impacto pode promover um efeito colateral grande na sua empresa.
Em alguns momentos tivemos a dúvida de qual era a diferença de “refactor” e “modification”. O “refactor” exige teste unitário. Caso algum código seja refatorado e não tenha teste unitário, ainda que a regra de negócio permaneça a mesma, esse deverá ser julgado como uma “modification.” Para evitar isso você pode escrever os testes unitários do código que você quer refatorar em uma branch diferente, submeter uma pull request e depois que esta estiver aprovada fazer o devido refactor.
Guia de commit
Uma vez definido o impacto de uma modificação, criamos um guia para os commits, branches e revisão de código. A ideia do símbolo é identificar o tipo do commit exclusivamente através do título. O impacto ajuda na revisão de código dizendo se a pull request exige ou não um olhar mais clínico.
Por fim, adicionei um commit de exemplo de como escrevemos um commit e como damos nomes a branches. Nele explico o que foi feito deixando todas as referências. Esse commit modificou 140 linhas em 10 arquivos.
Essa definição ajudou muito no nosso processo de revisão de código e foi fortemente inspirada em uma lightning talk do Mikhail Matrosov na CppCon. Deixo aqui essa excelente referência: https://www.youtube.com/watch?v=fzmjXK9JZ9o