https://a.storyblok.com/f/270183/139502/4ce40d3789/making-projects-better_part-one.png

Melhore seu projeto de software – Parte 1: Entendendo uma base de código

Publicado em November 15, 2022

Tempo de leitura: 8 minutos

Você já assumiu o controle de uma base de código e percebeu que não está satisfeito com a forma como o código está escrito ou organizado? É uma situação comum, mas que pode causar muitas dores de cabeça. A dívida técnica pode crescer como uma bola de neve, tornando exponencialmente mais difícil entender o código e adicionar novos recursos.

Nesta série de três partes, vou abordar alguns dos principais passos que você vai querer seguir para ficar mais satisfeito com seu (antigo) projeto. Para dar alguns exemplos concretos, vou integrar tudo explicando como refatorei e aprimorei o SDK do Vonage em Python de código aberto SDK do Vonage para Python, uma biblioteca que faz chamadas HTTP às APIs da Vonage, mas os princípios se aplicam a qualquer tipo de projeto de software.

Os exemplos desta postagem serão escritos em Python, mas esses princípios se aplicam a projetos em qualquer linguagem. Há também uma lista de verificação útil a ser seguida se você estiver tentando corrigir especificamente um projeto em Python.

A série, em seções

  1. Parte 1: Entendendo uma base de código (este artigo)

  2. Parte II: Fazendo mudanças

  3. Parte III: Aprimoramentos de nível avançado

O que essa série aborda?

Nesta série, vamos falar sobre:

  1. Entendendo o código

  2. Ganhar confiança para fazer mudanças e lidar com a dívida técnica

  3. Como construir confiança com seu chefe, sua equipe e seus clientes/comunidade

  4. Realizando melhorias

  5. O que fazer quando chega a hora de entregar o projeto

Ao final de cada artigo, você terá algumas estratégias para lidar com essa situação por conta própria e se sentirá capaz de fazer exatamente isso!

Sem mais delongas, vamos começar a Parte Um...

Parte I: Compreendendo o projeto

Leia coisas!

A primeira coisa a fazer é tentar entender o que o código que você herdou faz e como ele está organizado.

Comece conversando com qualquer pessoa que tenha conhecimento do projeto e lendo toda a documentação disponível. No meu caso, havia um arquivo “readme” no repositório que serviu como meu ponto de partida. Ele forneceu uma boa visão geral do estado do código na última atualização.

Image of readme

Havia também a documentação do produto, que me ajudou a entender o que se esperava que o código fizesse naquele momento, já que descrevia as APIs que eu precisava chamar e seu comportamento atual.

Image of main docs page

Crie coisas!

Depois de ter uma ideia do que o código faz, o próximo passo é explorá-lo criando algum tipo de projeto “Hello, World” — um projeto simples que utilize o código e faça algo pequeno, mas útil. Como meu projeto acessa APIs, escrevi um trecho de código bem simples que me envia um SMS.

import vonage


client = vonage.Client(key=MY_KEY, secret=MY_SECRET)

client.sms.send_message({
    "from": "Max",
    "to": MY_NUMBER,
    "text": "Hello, world!",
})

Deu certo!

Screenshot of my phone with a new message

Brinque com seu código e crie seu próprio “Hello, World”.

Entenda como o código está estruturado

É importante entender como o código do projeto está organizado para que você consiga visualizar mais facilmente o que está acontecendo quando seu “Hello, World” é executado. Essa também é uma ótima maneira de praticar suas habilidades de pensamento arquitetônico, já que você precisará desenvolver um modelo mental de como o código funciona. Essa forma de pensar ajudará você a dividir o projeto em partes distintas e mais fáceis de entender, o que também será útil mais adiante, quando você quiser reduzir o acoplamento de código e outros efeitos colaterais.

No caso do nosso SDK, o código foi organizado em seis arquivos separados (Python é uma linguagem muito compacta; a base de código equivalente em Java é cerca de 10 vezes maior!), e ficou assim:

  • Um arquivo para inicializar o projeto e lidar com as importações,

  • Um arquivo para conter métodos internos,

  • Um arquivo que continha classes de erro personalizadas, e

  • Três arquivos, cada um contendo uma classe relacionada a uma das APIs da Vonage

Image showing the different files with the descriptions above applied to them

Comecei a ficar preocupado quando percebi que havia três arquivos com nomes de APIs da Vonage, mas o arquivo README afirmava que eram suportadas 12 APIs diferentes.

Percebi que a maior parte do código estava em um arquivo (__init__.py) normalmente usado apenas para importações, em uma grande classe que lidava com tudo. Como isso não ajudava em nada na estrutura, decidi examinar a estrutura dos testes para obter mais informações sobre como o código estava organizado.

Os testes foram agrupados de forma sensata em módulos, o que me ajudou a compreender os diferentes componentes envolvidos. Recomendo tentar entender a estrutura tanto do seu código quanto dos seus testes, pois ambos podem ser muito esclarecedores.

Screenshot of the tests folder

Configure o ambiente de desenvolvimento e execute os testes!

Agora, é hora de instalar as dependências do projeto e executar os testes. Se eles forem aprovados, usando a versão mais recente da sua linguagem de programação e das dependências, ótima notícia! Os meus não passaram, então comecei com as versões exatas das dependências mencionadas e fui atualizando-as gradualmente para descobrir quais não estavam funcionando bem com a versão mais recente da minha linguagem de programação.

Nessa situação, atualize as dependências gradualmente e os problemas devem vir à tona. É provável que uma de suas dependências tenha lançado uma versão com uma alteração incompatível desde a última vez em que a base de código foi usada. (No meu caso, uma dependência mudou a forma como retornava os dados entre as versões, então tive que reescrever alguns testes para lidar com os dados corretamente.)

Ferramentas que podem ajudá-lo

Esta seção menciona algumas maneiras de utilizar ferramentas para ganhar ritmo em um projeto. O uso de ferramentas de análise de código pode ser extremamente útil, pois pode ser automatizado, o que significa que você pode executá-las quantas vezes quiser e acompanhar seu progresso à medida que começa a aprimorar a base de código.

A ferramenta também é ótima para o planejamento do trabalho, pois as informações que ela fornece indicam onde estão a dívida técnica e os pontos críticos do código, além de sugerir como você pode priorizar seu tempo!

Ferramentas de análise

A análise estática é uma forma de analisar automaticamente o código-fonte sem precisar executá-lo. Ela pode fornecer informações sobre a estrutura de uma base de código, destacar duplicações e outros pontos a serem refatorados, além de alertar sobre possíveis vulnerabilidades. Existem ferramentas gratuitas disponíveis online para a maioria das linguagens, e muitos provedores que cobram pelo serviço oferecem um plano gratuito para projetos não comerciais ou de código aberto, por exemplo, sonarcloud.

A análise comportamental refere-se ao estudo de um projeto com base no histórico de commits. Isso permite saber quem trabalhou no projeto e quando, o que foi alterado e quais componentes costumam ser alterados em conjunto, além de uma série de outras informações. Esse é um método realmente útil para projetos de grande porte com muitos colaboradores. O CodeScene oferece um plano gratuito para projetos de código aberto e funciona bem.

Cobertura de testes

A cobertura de teste (a porcentagem de instruções de código cobertas pelos seus testes) é útil para determinar exatamente o que seus testes de unidade estão verificando. Ela também pode destacar áreas de uma base de código que não foram testadas. Existem ferramentas disponíveis na maioria das linguagens; para Python, recomendo coverage.

Image of test coverage outputs

Pontuação de mutação

A cobertura de testes pode indicar qual a porcentagem do seu código que está coberta pelos testes, mas isso não diz o quanto seus testes são eficazes para realmente testar o comportamento do seu código! Os testes de mutação oferecem uma visão mais clara sobre o quanto você pode confiar que seus testes cumpram sua função e garantam que o seu código funcione conforme o esperado.

Ele funciona pegando instruções do seu código e alterando-as ligeiramente — por exemplo, alterando uma string ou substituindo um sinal de mais por um de menos, etc. — para gerar várias versões “mutantes” do seu código. Seu conjunto de testes é então executado em cada uma dessas versões mutantes do seu código. Como houve alterações, esperamos que os testes falhem — dizemos que capturamos o mutante. Mas se seus testes forem aprovados apesar dessas alterações, o mutante escapou e essas pequenas alterações poderiam ter chegado à produção! Assim, a índice de mutação (proporção de mutantes que detectamos em relação ao número total que foi criado) nos diz quanta confiança devemos ter em nossos testes.

Existem versões disso em vários idiomas, incluindo Stryker , que oferece suporte a JavaScript, Node.js e C#. Em Python, recomendo experimentar o o mutmut, que é simples e eficaz.

Image of mutation score output

Entenda a pilha de chamadas analisando seu exemplo!

Eu recomendaria tentar entender a pilha de chamadas que ocorre quando seu “Hello, World” é executado. Existem ferramentas na maioria das linguagens de programação para fazer isso.

Em Python, costumo recomendar uma ferramenta chamada Snakeviz para exibir visualmente sua pilha de chamadas. Coloque seu código “Hello, World” em uma função e faça o perfil dele, assim:

with cProfile.Profile() as pr:
    send_sms() # This function is where the Hello, World code lives
stats = pstats.Stats(pr)
stats.dump_stats(filename='send_sms.prof')

Isso gera um arquivo chamado send_sms.prof. Se você executar isso na linha de comando com o Snakeviz...

python -m pip install snakeviz snakeviz send_sms.prof

...isso vai gerar um gráfico interativo do tipo “icicle” que mostra todas as funções que a sua função chama, todas as funções que chamam, e assim por diante. Isso pode ser útil para ajudá-lo a acompanhar o caminho que o computador percorre pelo código e pode esclarecer como ele funciona.

Image of an icicle plot of my profiled function

E agora?

Se você seguir as sugestões deste artigo, estará em ótima posição para começar a construir confiança, lidar com a dívida técnica e fazer alterações em sua base de código. Volte em breve para conferir a Parte 2, onde falaremos sobre tudo isso em detalhes.

Enquanto isso, você pode entrar em contato conosco pela nossa Slack da Comunidade Vonage ou enviar uma mensagem pelo Twitter.

Compartilhar:

https://a.storyblok.com/f/270183/400x400/92109caf6a/max-kahan.png
Max KahanEx-funcionários da Vonage

Max é um ex-membro da equipe da Vonage. Ele atuou como Promotor de Desenvolvedores Python e Engenheiro de Software, com interesse em APIs de comunicação, aprendizado de máquina, experiência do desenvolvedor e dança! Ele é formado em Física, mas atualmente trabalha em projetos de código aberto e cria soluções para facilitar a vida dos desenvolvedores.