The Codest
  • Sobre nós
  • Serviços
    • Desenvolvimento de software
      • Desenvolvimento de front-end
      • Desenvolvimento backend
    • Staff Augmentation
      • Programadores Frontend
      • Programadores de back-end
      • Engenheiros de dados
      • Engenheiros de nuvem
      • Engenheiros de GQ
      • Outros
    • Aconselhamento
      • Auditoria e consultoria
  • Indústrias
    • Fintech e Banca
    • E-commerce
    • Adtech
    • Tecnologia da saúde
    • Fabrico
    • Logística
    • Automóvel
    • IOT
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • A nossa equipa
  • Case Studies
  • Saber como
    • Blogue
    • Encontros
    • Webinars
    • Recursos
Carreiras Entrar em contacto
  • Sobre nós
  • Serviços
    • Desenvolvimento de software
      • Desenvolvimento de front-end
      • Desenvolvimento backend
    • Staff Augmentation
      • Programadores Frontend
      • Programadores de back-end
      • Engenheiros de dados
      • Engenheiros de nuvem
      • Engenheiros de GQ
      • Outros
    • Aconselhamento
      • Auditoria e consultoria
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • A nossa equipa
  • Case Studies
  • Saber como
    • Blogue
    • Encontros
    • Webinars
    • Recursos
Carreiras Entrar em contacto
Seta para trás VOLTAR
2021-05-06
Desenvolvimento de software

Como não matar um projeto com más práticas de codificação?

The Codest

Bartosz Slysz

Software Engineer

Muitos programadores em início de carreira consideram que o tema da atribuição de nomes a variáveis, funções, ficheiros e outros componentes não é muito importante. Como resultado, a sua lógica de conceção é muitas vezes correta - os algoritmos são executados rapidamente e produzem o efeito desejado, embora possam ser pouco legíveis. Neste artigo, tentarei descrever brevemente o que nos deve guiar ao nomear diferentes elementos de código e como não ir de um extremo a outro.

Por que razão negligenciar a fase de nomeação prolongará (em alguns casos - enormemente) o desenvolvimento do seu projeto?

Vamos supor que tu e o teu equipa estão a tomar conta do código de outros programadores. O projeto you inherit foi desenvolvido sem qualquer tipo de amor - funcionou muito bem, mas cada um dos seus elementos poderia ter sido escrito de uma forma muito melhor.

No que diz respeito à arquitetura, no caso da herança de código, esta desencadeia quase sempre o ódio e a raiva dos programadores que a obtiveram. Por vezes, isto deve-se à utilização de tecnologias moribundas (ou extintas), por vezes à forma errada de pensar na aplicação no início do desenvolvimento e, por vezes, simplesmente devido à falta de conhecimentos do programador responsável.

Em qualquer caso, à medida que o tempo do projeto passa, é possível chegar a um ponto em que os programadores ficam furiosos com as arquitecturas e tecnologias. Afinal, todas as aplicações precisam de ser reescritas nalgumas partes ou apenas alteradas em partes específicas ao fim de algum tempo - é natural. Mas o problema que vai deixar os programadores de cabelos brancos é a dificuldade em ler e compreender o código que herdaram.

Especialmente em casos extremos, quando as variáveis são nomeadas com letras simples e sem sentido e as funções são um súbito surto de criatividade, de forma alguma consistente com o resto da aplicação, os seus programadores podem ficar loucos. Nesse caso, qualquer análise de código que possa ser executada de forma rápida e eficiente com a nomeação correta requer uma análise adicional dos algoritmos responsáveis pela produção do resultado da função, por exemplo. E essa análise, embora discreta, desperdiça uma enorme quantidade de tempo.

Implementar novas funcionalidades ao longo de diferentes partes da aplicação significa passar pelo pesadelo de a analisar, depois de algum tempo tem de voltar ao código e analisá-lo novamente porque as suas intenções não são claras, e o tempo anterior gasto a tentar compreender o seu funcionamento foi um desperdício porque já não se lembra qual era o seu objetivo.

E assim, somos sugados por um tornado de desordem que reina na aplicação e consome lentamente todos os participantes do seu desenvolvimento. Os programadores detestam o projeto, os gestores de projeto detestam explicar porque é que o tempo de desenvolvimento começa a aumentar constantemente, e o cliente perde a confiança e fica furioso porque nada corre como planeado.

Como evitá-lo?

Sejamos realistas - algumas coisas não podem ser ignoradas. Se tivermos escolhido certas tecnologias no início do projeto, temos de estar conscientes de que, com o tempo, deixarão de ser suportadas ou cada vez menos programadores serão fluentes em tecnologias de há alguns anos atrás que estão lentamente a tornar-se obsoletas. Algumas bibliotecas, nas suas actualizações, exigem alterações mais ou menos profundas no código, o que muitas vezes implica um turbilhão de dependências em que se pode ficar ainda mais preso.

Por outro lado, o cenário não é assim tão negro; é claro que as tecnologias estão a envelhecer, mas o fator que definitivamente atrasa o tempo de desenvolvimento dos projectos que as envolvem é, em grande parte, o código feio. E, claro, não podemos deixar de mencionar aqui o livro de Robert C. Martin - trata-se de uma bíblia para programadores, onde o autor apresenta uma série de boas práticas e princípios que devem ser seguidos para criar um código que procure a perfeição.

  1. O mais importante ao atribuir nomes às variáveis é transmitir de forma clara e simples a sua intenção. Parece bastante simples, mas por vezes é negligenciado ou ignorado por muitas pessoas. Um bom nome especificará exatamente o que é suposto a variável armazenar ou o que é suposto a função fazer - não pode ser um nome demasiado genérico, mas, por outro lado, não pode tornar-se uma longa lesma cuja mera leitura constitui um desafio para o cérebro. Depois de algum tempo com boas código de qualidadeQuando o utilizador é chamado a uma função, experimentamos o efeito de imersão, em que somos capazes de organizar subconscientemente a atribuição de nomes e a passagem de dados para a função de tal forma que tudo isto não deixa ilusões quanto à intenção que a impulsiona e ao resultado esperado da sua chamada.
  2. Outra coisa que pode ser encontrada em JavaScriptentre outras coisas, é uma tentativa de otimizar excessivamente o código, o que em muitos casos o torna ilegível. É normal que alguns algoritmos exijam cuidados especiais, o que muitas vezes reflecte o facto de a intenção do código poder ser um pouco mais complicada. No entanto, são extremamente raros os casos em que precisamos de optimizações excessivas, ou pelo menos aqueles em que o nosso código está sujo. É importante lembrar que muitas optimizações relacionadas com a linguagem têm lugar a um nível ligeiramente inferior de abstração; por exemplo, o motor V8 pode, com iterações suficientes, acelerar significativamente os loops. O que deve ser enfatizado é o facto de vivermos no século XXI e não escrevermos programas para a missão Apollo 13. Temos muito mais espaço de manobra em matéria de recursos - eles estão lá para serem usados (de preferência de forma razoável :>).
  3. Por vezes, dividir o código em partes dá muito jeito. Quando as operações formam uma cadeia cujo objetivo é realizar acções responsáveis por uma modificação específica dos dados, é fácil perder-se. Por isso, de uma forma simples, em vez de fazer tudo numa única cadeia, pode dividir as partes individuais do código que são responsáveis por uma determinada coisa em elementos individuais. Isto não só tornará clara a intenção das operações individuais, como também lhe permitirá testar fragmentos de código que são responsáveis apenas por uma coisa e que podem ser facilmente reutilizados.

Alguns exemplos práticos

Penso que a representação mais correta de algumas das afirmações acima será mostrar como funcionam na prática - neste parágrafo, tentarei delinear algumas más práticas de código que podem ser mais ou menos transformadas em boas práticas. Vou apontar o que perturba a legibilidade do código em alguns momentos e como o evitar.

A desgraça das variáveis de uma só letra

Uma prática terrível que, infelizmente, é bastante comum, mesmo nas universidades, é nomear as variáveis com uma única letra. É difícil não concordar que, por vezes, é uma solução bastante conveniente - evitamos pensar desnecessariamente em como determinar o objetivo de uma variável e, em vez de utilizarmos vários ou mais caracteres para a nomear, utilizamos apenas uma letra - por exemplo, i, j, k.

Paradoxalmente, algumas definições destas variáveis são dotadas de um comentário muito mais longo, que determina o que o autor tinha em mente.

Um bom exemplo aqui seria representar a iteração sobre uma matriz bidimensional que contém os valores correspondentes na intersecção da coluna e da linha.

const array = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];

// muito mau
for (let i = 0; i < array[i]; i++) {
  for (let j = 0; j < array[i][j]; j++) {
    // aqui está o conteúdo, mas toda vez que i e j são usados eu tenho que voltar e analisar para que eles são usados
  }
}

// ainda é mau mas é engraçado
let i; // linha
let j; // coluna

for (i = 0; i < array[i]; i++) {
  for (j = 0; j < array[i][j]; j++) {
    // aqui está o conteúdo, mas sempre que i e j são utilizados tenho de voltar atrás e verificar nos comentários para que são utilizados
  }
}

// muito melhor
const rowCount = array.length;

for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
  const row = array[rowIndex];
  const columnCount = row.length;

  for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
    const column = row[columnIndex];

    // alguém tem dúvidas sobre o que é o quê?
  }
}

Sobre-otimização sorrateira

Um belo dia, deparei-me com um código altamente sofisticado escrito por um engenheiro de software. Este engenheiro tinha descoberto que o envio de permissões de utilizador como cadeias de caracteres especificando acções específicas podia ser grandemente optimizado utilizando alguns truques ao nível dos bits.

Provavelmente, esta solução seria aceitável se o alvo fosse o Commodore 64, mas o objetivo deste código era uma simples aplicação web, escrita em JS. Chegou a altura de ultrapassar esta peculiaridade:
Digamos que um utilizador tem apenas quatro opções em todo o sistema para modificar o conteúdo: criar, ler, atualizar, eliminar. É bastante natural que enviemos estas permissões num formato JSON como chaves de um objeto com estados ou uma matriz.

No entanto, o nosso inteligente engenheiro reparou que o número quatro é um valor mágico na apresentação binária e descobriu-o da seguinte forma:

maus hábitos de programação

A tabela completa de capacidades tem 16 linhas, mas eu listei apenas 4 para transmitir a ideia de criar estas permissões. A leitura das permissões é feita da seguinte forma:

const user = { permissions: 11 };

const canCreate = Boolean(user.permissions & 8); // true
const canRead = Boolean(user.permissions & 4); // false
const canUpdate = Boolean(user.permissions & 2); // true
const canDelete = Boolean(user.permissions & 1); // true

O que vê acima não é Código WebAssembly. Não quero ser mal interpretado aqui - tais optimizações são normais para sistemas onde certas coisas precisam de ocupar muito pouco tempo ou memória (ou ambos). As aplicações Web, por outro lado, não são definitivamente um lugar onde tais optimizações excessivas façam todo o sentido. Não quero generalizar, mas no trabalho dos programadores de front-end raramente são efectuadas operações mais complexas que atinjam o nível de abstração de bits.

Simplesmente não é legível, e um programador que possa fazer uma análise de tal código perguntar-se-á certamente que vantagens invisíveis tem esta solução e o que pode ser danificado quando o equipa de desenvolvimento quer reescrevê-lo numa solução mais razoável.

Além disso, suspeito que o envio das permissões como um objeto normal permitiria a um programador ler a intenção em 1 a 2 segundos, ao passo que a análise de todo este processo desde o início demoraria pelo menos alguns minutos. Haverá vários programadores no projeto, e cada um deles terá de se deparar com este pedaço de código - terão de o analisar várias vezes, porque ao fim de algum tempo esquecer-se-ão da magia que ali se passa. Vale a pena guardar esses poucos bytes? Na minha opinião, não.

Dividir e conquistar

Desenvolvimento Web está a crescer rapidamente e não há qualquer indicação de que algo vá mudar em breve a este respeito. Temos de admitir que a responsabilidade dos programadores front-end aumentou significativamente nos últimos tempos - eles assumiram a parte da lógica responsável pela apresentação dos dados na interface do utilizador.

Por vezes, esta lógica é simples e os objectos fornecidos pela API têm uma estrutura simples e legível. Por vezes, porém, requerem diferentes tipos de mapeamento, ordenação e outras operações para os adaptar a diferentes locais da página. E é neste ponto que podemos facilmente cair no pântano.

Muitas vezes, dei por mim a tornar os dados das operações que estava a efetuar praticamente ilegíveis. Apesar da utilização correta de métodos de array e da nomeação adequada de variáveis, as cadeias de operações em alguns pontos quase perderam o contexto do que eu queria alcançar. Além disso, algumas destas operações precisavam de ser utilizadas noutros sítios e, por vezes, eram suficientemente globais ou sofisticadas para exigir a realização de testes.

const devices = [
  { id: '001', version: 1, owner: { location: 'Texas', age: 21 } },
  { id: '002', version: 2, owner: { location: 'Texas', age: 27 } },
  { id: '003', version: 3, owner: { location: 'Arizona', age: 27 } },
  { id: '004', version: 2, owner: { location: 'Chicago', age: 24 } },
  { id: '005', version: 2, owner: { location: 'Arizona', age: 19 } },
  { id: '006', version: 3, owner: { location: 'Texas', age: 42 } },
];

// bastante complicado
(() => {
  const locationAgeMap = dispositivos
    .map(device => device.owner)
    .reduce((map, item) => {
      se (!map[item.location]) {
        map[item.location] = item.age;
      } else {
        map[item.location] += item.age;
      }

      return map;
    }, {});

  const locationLegalAgeOwnersMap = dispositivos
    .map(dispositivo => dispositivo.proprietário)
    .filter(owner => owner.age >= 21)
    .reduce((map, item) => {
      se (!map[item.location]) {
        map[item.location] = [item];
      } else {
        map[item.location].push(item);
      }

      return map;
    }, {});

  consola.log({ locationAgeMap, locationLegalAgeOwnersMap });
})();

// ainda complicado mas mais compacto
(() => {
  const locationOwnersMap = dispositivos
  .map(device => device.owner)
  .reduce((map, item) => {
    se (!map[item.location]) {
      map[item.location] = [item];
    } else {
      map[item.location].push(item);
    }

    return map;
  }, {});

  const locationAgeMap = Object.fromEntries(
    Object.entries(locationOwnersMap).map(([location, owners]) => {
      const ownersAge = owners.map(owner => owner.age).reduce((a, b) => a + b);

      return [location, ownersAge];
    })
  );

  const locationLegalAgeOwnersMap = Object.fromEntries(
    Object.entries(locationOwnersMap).map(([localização, proprietários]) => {
      const filteredOwners = owners.filter(owner => owner.age >= 21);

      return [localização, filteredOwners];
    }).filter(([_location, owners]) => {
      return owners.length > 0;
    })
  );

  consola.log({ locationAgeMap, locationLegalAgeOwnersMap });
})();

Eu sei, eu sei - este não é um pedaço de código trivial que ilustra facilmente o que quero transmitir. E também sei que as complexidades computacionais dos dois exemplos são ligeiramente diferentes, embora em 99% dos casos não precisemos de nos preocupar com isso. A diferença entre os algoritmos é simples, pois ambos preparam um mapa de localizações e proprietários de dispositivos.

O primeiro prepara este mapa duas vezes, enquanto o segundo o prepara apenas uma vez. E o exemplo mais simples que mostra nós que o segundo algoritmo é mais portátil reside no facto de termos de alterar a lógica de criação deste mapa para o primeiro e, por exemplo, fazer a exclusão de certas localizações ou outras coisas estranhas chamadas lógica empresarial. No caso do segundo algoritmo, modificamos apenas a forma de obter o mapa, enquanto todas as restantes modificações de dados que ocorrem nas linhas subsequentes permanecem inalteradas. No caso do primeiro algoritmo, temos de ajustar todas as tentativas de preparação do mapa.

E isto é apenas um exemplo - na prática, há muitos casos em que é necessário transformar ou refactorizar um determinado modelo de dados em toda a aplicação.

A melhor maneira de evitar acompanhar as várias mudanças de negócio é preparar ferramentas globais que nos permitam extrair informações de interesse de uma forma bastante genérica. Mesmo com o custo desses 2-3 milissegundos que podemos perder à custa da desoptimização.

Resumo

Ser programador é uma profissão como qualquer outra - todos os dias aprendemos coisas novas, muitas vezes cometendo muitos erros. O mais importante é aprender com esses erros, tornar-se melhor na sua profissão e não repetir esses erros no futuro. Não se pode acreditar no mito de que o trabalho que fazemos será sempre impecável. Pode, no entanto, com base nas experiências dos outros, reduzir as falhas em conformidade.

Espero que a leitura deste artigo o ajude a evitar, pelo menos, algumas das más práticas de codificação que experimentei no meu trabalho. Em caso de dúvidas sobre as melhores práticas de código, pode contactar Tripulação The Codest sair para consultar as suas dúvidas.

Ler mais:

Estratégias de obtenção de dados no NextJS

Segurança das aplicações Web - vulnerabilidade XSS

Segurança das aplicações Web. Vulnerabilidade Target="_blank

*O gráfico do título é da speednet.pl

Artigos relacionados

Ilustração de uma aplicação de cuidados de saúde para smartphone com um ícone de coração e um gráfico de saúde em ascensão, com o logótipo The Codest, representando soluções digitais de saúde e HealthTech.
Desenvolvimento de software

Softwares para o setor de saúde: Tipos, casos de uso

As ferramentas em que as organizações de cuidados de saúde confiam atualmente não se assemelham em nada às fichas de papel de há décadas atrás. O software de cuidados de saúde apoia agora os sistemas de saúde, os cuidados aos doentes e a prestação de cuidados de saúde modernos em...

OCODEST
Ilustração abstrata de um gráfico de barras em declínio com uma seta ascendente e uma moeda de ouro que simboliza a eficiência ou a poupança de custos. O logótipo The Codest aparece no canto superior esquerdo com o slogan "In Code We Trust" sobre um fundo cinzento claro
Desenvolvimento de software

Como dimensionar a sua equipa de desenvolvimento sem perder a qualidade do produto

Aumentar a sua equipa de desenvolvimento? Saiba como crescer sem sacrificar a qualidade do produto. Este guia cobre sinais de que é hora de escalar, estrutura da equipe, contratação, liderança e ferramentas - além de como o The Codest pode...

OCODEST
Desenvolvimento de software

Construir aplicações Web preparadas para o futuro: ideias da equipa de especialistas do The Codest

Descubra como o The Codest se destaca na criação de aplicações web escaláveis e interactivas com tecnologias de ponta, proporcionando experiências de utilizador perfeitas em todas as plataformas. Saiba como a nossa experiência impulsiona a transformação digital e o negócio...

OCODEST
Desenvolvimento de software

As 10 principais empresas de desenvolvimento de software sediadas na Letónia

Saiba mais sobre as principais empresas de desenvolvimento de software da Letónia e as suas soluções inovadoras no nosso último artigo. Descubra como estes líderes tecnológicos podem ajudar a elevar o seu negócio.

thecodest
Soluções para empresas e escalas

Fundamentos do desenvolvimento de software Java: Um Guia para Terceirizar com Sucesso

Explore este guia essencial sobre o desenvolvimento de software Java outsourcing com sucesso para aumentar a eficiência, aceder a conhecimentos especializados e impulsionar o sucesso do projeto com The Codest.

thecodest

Subscreva a nossa base de conhecimentos e mantenha-se atualizado sobre os conhecimentos do sector das TI.

    Sobre nós

    The Codest - Empresa internacional de desenvolvimento de software com centros tecnológicos na Polónia.

    Reino Unido - Sede

    • Office 303B, 182-184 High Street North E6 2JA
      Londres, Inglaterra

    Polónia - Pólos tecnológicos locais

    • Parque de escritórios Fabryczna, Aleja
      Pokoju 18, 31-564 Cracóvia
    • Embaixada do Cérebro, Konstruktorska
      11, 02-673 Varsóvia, Polónia

      The Codest

    • Início
    • Sobre nós
    • Serviços
    • Case Studies
    • Saber como
    • Carreiras
    • Dicionário

      Serviços

    • Aconselhamento
    • Desenvolvimento de software
    • Desenvolvimento backend
    • Desenvolvimento de front-end
    • Staff Augmentation
    • Programadores de back-end
    • Engenheiros de nuvem
    • Engenheiros de dados
    • Outros
    • Engenheiros de GQ

      Recursos

    • Factos e mitos sobre a cooperação com um parceiro externo de desenvolvimento de software
    • Dos EUA para a Europa: Porque é que as empresas americanas decidem mudar-se para a Europa?
    • Comparação dos centros de desenvolvimento da Tech Offshore: Tech Offshore Europa (Polónia), ASEAN (Filipinas), Eurásia (Turquia)
    • Quais são os principais desafios dos CTOs e dos CIOs?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Direitos de autor © 2026 por The Codest. Todos os direitos reservados.

    pt_PTPortuguese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese es_ESSpanish nl_NLDutch etEstonian elGreek cs_CZCzech pt_PTPortuguese