Uma afirmação popular sobre código é que ele é lido dez vezes mais do que escrito. Isso geralmente é usado como um argumento para codificação exigente: você verá muito seu código, então gaste algum tempo extra em qualidade . A evidência revisada por pares para essa afirmação é escassa, mas tudo o que consegui encontrar mostra a proporção de 10 para 1 como extremamente conservadora. A proporção real talvez esteja mais próxima de 100 para 1. E quanto maior a vida útil de um produto, maior a proporção.
À luz dessas informações, parece que investimos pouco na habilidade de entender o código. Livros e tutoriais geralmente se concentram na arte do código, a capacidade do programador de teorizar, escrever e modificar código de maneira eficaz e legível, em vez da atividade muito mais comum de ler e interpretar código que já existe. É verdade que ler código é uma habilidade que está abaixo de escrevê-lo – se você pode escrever Python, é lógico que você pode entendê-lo. Mas ler código também é uma habilidade por si só.
Os novos desenvolvedores às vezes sentem que, se não gastarem a maior parte do tempo adicionando novo código a um projeto, não estão sendo produtivos. Essa atitude pode realmente retê-lo. A codificação eficaz requer contexto e confiança: você precisa entender o ambiente em que seu código viverá e ter certeza de que seu trabalho agrega valor. Os primeiros 80 a 95% do tempo gasto em uma tarefa devem ser gastos lendo código e outras formas de documentação. Às vezes é até 100% – no processo de estudar o código existente, você pode aprender o suficiente para poder dizer “esse recurso já existe, acabamos de nos esquecer dele” ou “isso fará mais mal do que bem”.
Ler código é demorado e muitas vezes chato também. Pode envolver perseguições tediosas em tocas de coelho, suposições e verificações repetitivas e vasculhar longas listas de resultados de pesquisa. É um trabalho disciplinado e ingrato. Mas é tempo bem gasto e não há necessidade de pressa. E com a abordagem correta, não precisa ser um fardo.
Neste artigo, explicarei as táticas de leitura de código mais práticas que aprendi ao longo da minha carreira. Todas são ferramentas úteis para se ter em seu cinto, e você vai misturá-las e combiná-las dependendo da situação.
1. Instale plugins úteis
Seu IDE é uma ferramenta inestimável para entender o código. Editores como Visual Studio, VS Code, Eclipse e IntelliJ IDEA vivem e morrem pela força de suas habilidades de análise de código e pelo tamanho de suas bibliotecas de plugins. Qualquer que seja o idioma, estrutura ou serviço de nuvem com o qual você esteja trabalhando, reserve alguns momentos para instalar os plug-ins associados. Isso ajudará você a navegar em sua base de código e a detectar problemas mais rapidamente. Plugins oficiais e originais são melhores se você puder encontrá-los, mas os plugins populares suportados pela comunidade também podem ser excelentes.
Procure os seguintes recursos:
Realce de sintaxe: mostra palavras-chave, nomes de classe/método/campo/variável e colchetes em cores diferentes para ajudar na compreensão.
Formatação automática: modifica o espaço em branco, o comprimento da linha e outros elementos de estilo para serem mais legíveis e consistentes. Normalmente, você pode configurar isso para acontecer em um atalho de teclado ou sempre que salvar um arquivo.
Análise estática: alerta você sobre problemas em seu código sem realmente executá-lo. Por exemplo, se você digitar incorretamente o nome de uma variável ou usar o tipo errado de aspas, as ferramentas de análise estática incorporadas ao seu IDE ou plug-in de idioma completo o sublinharão em vermelho.
Navegação contextual: fornece opções de menu como “Pular para definição”, “Ver implementações” e “Ver referências” quando você abre o menu de contexto (clique com o botão direito) em um identificador.
Refatoração: automatiza refatorações comuns, como extrair lógica para um método, alterar parâmetros de método ou renomear uma variável.
Dicas de código: mostra informações (como tipos, parâmetros e documentação manuscrita) sobre uma classe/método/campo quando você passa o cursor sobre ele.
Executor de teste: fornece uma interface do usuário para executar testes de unidade e integração e relata os resultados.
Depurador: permite definir pontos de interrupção em seu código para que você possa percorrer um processo específico uma linha por vez e inspecionar os valores no escopo.
Integração de controle de versão: ajuda você a sincronizar e mesclar código com o restante de sua equipe. Também fornece informações sobre o autor e a data da última edição de cada linha de código.
Você pode ler o código sem essas ferramentas e, às vezes, pode ser necessário. Mas as ferramentas específicas da linguagem facilitam a verificação de suas suposições e a coleta de contexto, e é mais provável que você faça o que é mais fácil. No final, melhores ferramentas geralmente significam menos adivinhação.
2. Leia o código pelo menos duas vezes
Uma leitura quase nunca é suficiente para entender completamente o que um pedaço de código está fazendo. Dois é o mínimo.
Em sua primeira leitura, tente entender o quadro geral examinando e construindo um esboço em sua mente (ou no papel, se ajudar). Seu objetivo é ser capaz de resumir o que o código faz. Se você tem um pato de borracha à mão, agora é a hora de começar a falar com ele. Explique o propósito geral do código em termos básicos. Se você estiver um pouco nebuloso em algumas partes, isso chamará a atenção para elas.
A segunda leitura é mais sobre detalhes. Certifique-se de entender cada linha de código ou pelo menos ter uma teoria sobre isso. Preste atenção especial aos efeitos externos. Quais métodos estão sendo chamados? Quais valores compartilhados estão sendo atualizados? Quais são os valores de retorno? Passe algum tempo mergulhando em cada um deles para que você possa entender toda a lógica em jogo, mesmo que esteja fora do código que você está estudando.
Clique em outros métodos ( ctrl + clique na maioria dos IDEs), passe o mouse sobre os métodos da biblioteca para ler a documentação, abra uma guia do navegador para verificar o que uma parte específica da sintaxe significa. Procure por instruções import, include ou using na parte superior do arquivo para descobrir quais namespaces e bibliotecas estão sendo usados. Ao terminar, você terá teorias sobre possíveis comportamentos, casos extremos e condições de falha no código.
Lembre-se de que não há mágica no código! Pode haver partes que você não entende, mas quase sempre seguem regras simples que você pode encontrar na documentação online ou aprender com um colega de equipe. É melhor aprender essas regras do que confiar em suposições.
Uma terceira leitura é valiosa se o código contiver lógica complexa. Escolha valores simples para quaisquer parâmetros ou variáveis e imagine-os fluindo pelo código de cima para baixo. Calcule os resultados de cada linha. Não tenha medo de buscar um REPL se estiver tendo dificuldade em visualizar uma parte da lógica.
Realisticamente, cada uma dessas leituras pode envolver várias passagens e vários desvios para o Google. É totalmente normal ler um pedaço de código dez vezes ou mais antes de realmente entendê-lo. Fazer uma longa pausa ou até mesmo uma soneca após as primeiras leituras pode ajudar, especialmente se você estiver lidando com conceitos que são novos para você.
3. Refatorar nomes de variáveis e métodos locais
Às vezes, um pedaço de código é tão vago ou enganoso que é difícil raciocinar. Uma maneira virtualmente livre de riscos de progredir é renomear variáveis locais e métodos privados para descrever com mais precisão o que eles fazem. Esses tipos de alterações não afetarão nada fora do arquivo em que você está trabalhando e não causarão erros lógicos, desde que você tome cuidado para evitar colisões de nomes. Se possível, use as ferramentas de refatoração do IDE (em vez de localizar e substituir texto) para renomear algo em todos os lugares em que for usado com um clique.
Por exemplo, considere o seguinte trecho de JavaScript:
function ib(a, fn) {
return (a || []).reduce((o, i) => {
o[fn(i)] = i;
return o;
}, {});
}
É muito difícil de ler e o nome ibé inútil para ajudar você a entender o que ele faz. Você pode fazer algumas inferências sobre isso, no entanto:
Como reduceestá sendo chamado a(e retorna a uma matriz vazia), adeve ser um tipo de matriz.
O argumento de retorno de chamada iserá um elemento dessa matriz.
O segundo argumento para reduce, um literal de objeto vazio {}, nos diz que o argumento de retorno de chamada oé um dicionário (objeto).
Então, um pouco de renomeação nos traz aqui:
function ib(array, fn) {
return (array || []).reduce((dict, element) => {
dict[fn(element)] = element;
return dict;
}, {});
}
Você pode ver agora que fné usado para transformar um elemento de matriz em uma chave de dicionário. E isso revela o propósito da função ib: transformar um array em um dicionário, usando um callback personalizado para determinar a chave que indexa cada elemento. Você pode renomear fnpara getKeypara maior clareza e ibdeve ser nomeado indexByou toDictionaryalgo assim. Quase qualquer coisa seria uma melhoria no que é atualmente nomeado.
Pode haver outras coisas que poderíamos melhorar nesta função, mas por enquanto estamos aqui apenas para interpretá-la. Renomear alguns identificadores nos ajudou a entender o código sem alterar sua lógica ou ter que pensar em todas as suas partes de uma só vez.
Cabe a você confirmar essas alterações. Considere fortemente. Melhorar a legibilidade do código beneficiará toda a equipe repetidamente, mesmo que não adicione ou altere a funcionalidade.
4. Veja como o código é usado
A maior parte do código é usado por outro código. Se você está lutando com um pedaço de código, mas entende uma situação em que ele é usado, isso pode ser um contexto valioso para descobrir o que ele está fazendo.
Idealmente, seu IDE permitirá que você clique com o botão direito do mouse no nome do método (ou clique em um botão de dica de contexto) e selecione “Ver referências”. Isso listará todos os locais onde o método é usado. Você pode então navegar por eles para um contexto que você entenda.
Se o seu IDE não tiver esse recurso, mas você estiver trabalhando em uma linguagem compilada ou transpilada, outro truque que você pode usar é renomear o método para algo ridículo como ThisBreaksOnPurpose. Erros de compilação informarão onde o método estava sendo usado, embora nos casos em que ele seja acessado por reflexão, você não verá um erro até o tempo de execução. Certifique-se de alterar o nome de volta depois.
Se nenhuma dessas opções for possível, você poderá retornar a uma pesquisa de texto para o nome do método. Se você tiver sorte, o método terá um nome exclusivo na base de código. Caso contrário, você pode acabar com um conjunto de resultados maior e ter que vasculhar muito código que não é relevante.
5. Procure um código semelhante
Às vezes, o código é difícil de entender, mesmo que todos os identificadores sejam bem nomeados e os casos de uso sejam familiares. Nem todo código é idiomático . Às vezes, não há idioma para uma operação específica. E, na pior das hipóteses, o código em questão é exclusivo da base de código em que você está trabalhando ou não há uma frase óbvia que você possa pesquisar no Google para saber mais sobre isso.
A boa notícia é que o código verdadeiramente único é raro em bases de código de longa duração, especialmente no grão de uma única expressão ou linha de código. Se você levar alguns minutos para procurar código semelhante no projeto, poderá encontrar algo que desbloqueie todo o quebra-cabeça.
A pesquisa de texto completo é a versão mais simples disso. Escolha um trecho de código que se destaque e cole-o no painel de pesquisa universal em seu IDE (geralmente vinculado ao atalho ctrl + shift + F ). As ferramentas de pesquisa geralmente incluem uma opção de pesquisa “palavra inteira”, o que significa que uma pesquisa por care.exenão retornará resultados como scare.exertion. Se quiser restringir ainda mais as coisas, você pode pesquisar com uma expressão regular em vez de uma frase de texto, o que é útil se você estiver procurando por algo como “um número em ambos os lados de um operador >>ou de um <<operador bit a bit”.
Ocasionalmente, mesmo um regex não reduz as coisas o suficiente, e ninguém quer passar várias horas vasculhando os resultados da pesquisa por algo que pode até não ajudar. Vale a pena aprender algumas técnicas avançadas de pesquisa.
Muitos programadores preferem ferramentas de linha de comando Unix como grep e awk ou, no Windows, scripts PowerShell escritos à mão. Minha escolha é JS Powered Search , uma extensão do VS Code que permite definir uma consulta de pesquisa lógica em JavaScript (divulgação completa: sou o autor de JSPS). Como eu escrevo JavaScript no meu trabalho diário, isso é o que é mais fácil para mim em uma pitada.
O objetivo é restringir a pesquisa a alguns arquivos com maior probabilidade de espelhar o processo que você está estudando. Depois de fazer isso, você tem outra perspectiva para entender o código.
6. Execute testes de unidade
Em uma base de código perfeita, testes de unidade seriam tudo o que você precisaria para entender o comportamento de qualquer seção de código. A maioria das bases de código não corresponde a esse ideal; por razões de eficiência, os testes tendem a ser vagos e, às vezes, descrevem comportamentos obsoletos. Ainda assim, é uma boa ideia verificar se há testes que executam o código que você está estudando. No mínimo, eles descreverão as entradas e saídas do código.
Se os testes de unidade não estiverem lá ou não forem abrangentes o suficiente, esta é uma segunda oportunidade para fazer algumas mudanças positivas. Você pode extrair o código para uma sandbox e executá-lo lá – às vezes essa é a jogada certa – mas, contanto que você esteja explorando seu comportamento, também pode usar um executor de testes.
Escreva um ou dois testes para responder às perguntas que você ainda tem sobre o código. Você pode confirmar suas alterações posteriormente, aumentando a estabilidade da base de código e tornando-a mais autodocumentada para qualquer outra pessoa que a encontre. Você nunca precisa se preocupar que adicionar um teste automatizado quebrará a funcionalidade existente.
Os testes levam tempo para serem escritos, mas são muito mais eficazes do que executar código em sua imaginação. Eles são evidências reais de que o código funciona de uma certa maneira. E se você acabar precisando modificar o código, seus testes lhe darão confiança de que você não o está quebrando.
7. Use o depurador
Depois de ter alguns testes de unidade (ou mesmo um simples que executa o código sem asserções), você tem uma ótima configuração para depuração passo a passo. Defina um ponto de interrupção (a maioria dos IDEs permite que você faça isso clicando ao lado do número da linha no editor de código) ou adicione uma instrução de ponto de interrupção/depurador na parte superior do código. Em seguida, execute o teste. Depois de atingir o ponto de interrupção, a execução será pausada e você poderá avançar uma linha por vez, entrar e sair de funções e inspecionar os valores de todas as variáveis no escopo.
Se você souber quais ações do usuário acionam o código em questão, você pode definir seu breakpoint e executar o programa normalmente, interagindo com sua interface para fazer o código rodar. O ciclo de feedback é mais longo se você fizer dessa maneira, mas também usa dados mais realistas, o que pode ajudá-lo a perceber coisas como referências nulas e casos extremos.
A depuração de cima para baixo pode ser menos útil para código que é executado dezenas ou centenas de vezes, como um loop aninhado. Para código como este, você pode querer adicionar variáveis que agregam dados em cada iteração para que você possa analisá-las posteriormente. Muitos IDEs também permitem definir pontos de interrupção condicionais (ou colocar uma instrução de ponto de interrupção em um ifbloco) para que você possa pausar durante uma iteração que atenda a determinados requisitos.
8. Pesquise a base de conhecimento
Se sua equipe usa uma base de conhecimento de programação, Confluence ou um wiki do GitHub, agora você deve ter uma boa ideia de quais termos ou conceitos você pode pesquisar para encontrar documentação relevante. Você pode pular para esta etapa muito mais cedo se sua equipe escrever a documentação como parte padrão do processo de desenvolvimento.
Tenha em mente que a documentação não deve ser sua única fonte de verdade – ela começa a ficar desatualizada no momento em que é publicada, e a única coisa em que você pode confiar totalmente para dizer como um pedaço de código se comporta é o próprio código. Ainda assim, mesmo a documentação desatualizada pode fornecer informações básicas e contexto suficientes para ajudá-lo a evitar conclusões erradas.
A documentação pode explicar o “como” de um trecho de código, mas geralmente é melhor explicar o “porquê”. Às vezes você entende o que um pedaço de código está fazendo, mas algo nele não parece certo. Antes de alterá-lo, você deve fazer todos os esforços para entender em quais informações ou restrições o programador original estava agindo.
Uma boa documentação interna também pode apontar para um colega de equipe que sabe o que está acontecendo. Se você chegou até aqui, já trabalhou mais do que o suficiente para justificar a busca por ajuda. Envie uma mensagem ou agende uma ligação com o colega de equipe, certificando-se de ser específico sobre no que você está trabalhando e qual problema está tentando resolver. Cole o código em questão para eles verem; há uma boa chance de que eles notem algo que você não notou. Se você tiver sorte, eles se lembrarão exatamente do que estavam fazendo e por quê, mas pelo menos eles devem ser capazes de ajudá-lo a descobrir.
9. Use anotação de controle de versão (git culpado)
Até agora você aprendeu tudo o que o próprio código lhe dirá, bem como tudo o que o Google, e a documentação de sua equipe lhe dirão. Você é um especialista. E mesmo assim pode haver peças faltando no quebra-cabeça: uma decisão de design bizarra, um método que quebra os padrões que o resto da base de código segue, um cheiro de código sem justificativa óbvia. Uma última maneira de reunir contexto é rastrear o autor original, a mensagem de confirmação e o ticket de gerenciamento de projeto associado a esse código.
Seu sistema de controle de versão (Git, Subversion, Mercurial ou o que você usar) tem uma ferramenta que revela o autor e o commit de qualquer linha de código na base de código. No Git, este é o git blamecomando. A maioria dos sistemas chama isso de “culpa” ou “anotação”.
Você pode executar isso na linha de comando ou em seu IDE. O que aparece será uma lista de commits linha por linha: um hash de commit, uma mensagem de commit e um autor. Ao procurar o hash de confirmação no controle de versão da sua equipe ou no aplicativo de gerenciamento de projetos, você poderá encontrar o pull request original que incluiu o código e, a partir daí, você pode seguir um link para o ticket original onde o recurso ou a correção do bug foi solicitado.
Se o commit mais recente para essa linha de código não for significativo – digamos, é uma formatação ou alteração de espaço em branco – talvez seja necessário examinar o histórico de alterações do arquivo para encontrar o commit em que a linha de código foi introduzida. Novamente, seu sistema de controle de versão possui ferramentas para ajudá-lo a fazer isso.
Uma vez que você tenha uma RP e um ticket em mãos, você não apenas terá um contexto valioso desde o momento em que o código foi escrito, mas também encontrará os nomes de todos que participaram dele: o autor do código, revisores de RP, qualquer pessoa que comentou ou atualizou o ticket, a pessoa que assinou o controle de qualidade. Qualquer uma dessas pessoas pode oferecer informações que o ajudem a cruzar a linha de chegada. Se você não conversou com alguém na etapa anterior, agora é a hora.
Entenda primeiro, escreva o código depois
O contexto e a compreensão que você adquiriu ao longo dessas etapas provavelmente serão valiosos no futuro. Antes de seguir em frente, considere refatorar o código para maior clareza, criar nova documentação ou até mesmo enviar um e-mail com suas descobertas. Sempre que você investir aqui, você e sua equipe irão interagir com o código no futuro.
A capacidade de ler o código com eficiência é uma arma secreta que o acelerará nas entrevistas técnicas e o tornará um membro essencial de qualquer equipe. Programadores que são bons em escrever código são valiosos, mas programadores que são bons em ler código são ainda mais valiosos. Quando há um bug na produção ou um recurso precisa ser construído com urgência, o primeiro e mais importante passo é a compreensão. Ler o código é o que vai te levar até lá.








