Executando verificação de segurança...
-1

Cinco principais princípios do desenvolvimento de software

Desenvolvimento de software é uma atividade complexa que envolve diversos processos, tais como análise, design, codificação, testes e manutenção. Para tornar esse processo mais eficiente e eficaz, foram criados diversos princípios que guiam os desenvolvedores na criação de sistemas de software de alta qualidade. Neste artigo, vamos abordar cinco desses princípios que eu considero como os principais: DRY, KISS, YAGNI, POLS e CoC.

Antes de tudo, é importante frisar que esses princípios são contextuais e não se aplicam a todas as situações. Segui-los cegamente faz de você um mau programador, tanto quanto não segui-los. Em vez de buscar a pureza dos princípios, procure a praticidade no desenvolvimento real e encontre o equilíbrio entre aplicá-los e lidar com as complexidades e desafios específicos de cada projeto. É preciso ter em mente que nem sempre será possível segui-los e que uma abordagem mais flexível e adaptável pode ser necessária.

DRY ("Don't Repeat Yourself")

Não repita-se!

Estimula a reutilização de código e a eliminação de duplicação no sistema. O princípio DRY afirma que cada parte do conhecimento em um sistema deve ter uma única codificação, representando uma única e confiável fonte de verdade.

Em outras palavras significa que você deve escrever seu código de forma modular, de modo que uma determinada funcionalidade ou comportamento seja definida apenas uma vez no sistema, de forma que se precisar ser alterada ou corrigida, você só precise fazer isso em um único lugar, em vez de procurar em todo o sistema onde a mesma funcionalidade é implementada.

Perceba que foi falado em “parte do conhecimento”, em diferentes arquiteturas podemos chamar isso de use-case, service, event, então não estamos falando do código em si, mas da repetição de regras de negócio.

Talvez não tenha ficado claro a diferença ainda então vamos aos exemplos:

  • Duplicação de regras de negócio

    Em um sistema de vendas online, onde a lógica para calcular o preço final do produto é implementada em mais de uma parte do código. Isso pode acontecer quando a lógica é repetida em diferentes partes do sistema, como no carrinho de compras e na página de finalização do pedido. Seguindo o princípio DRY, a lógica para calcular o preço final do produto deve ser implementada em um único lugar, e as diferentes partes do sistema devem referenciar essa implementação.

  • Duplicação de código

    Quando a mesma funcionalidade é implementada em diferentes partes do código, mas essas implementações evoluem de maneira independente. Por exemplo, imagine que um sistema de vendas online tenha duas páginas diferentes que permitem ao usuário adicionar um produto ao carrinho: uma na página de detalhes do produto e outra na página de resultados de busca. Embora essas duas páginas tenham funcionalidades semelhantes, suas implementações podem evoluir de maneira independente.

No exemplo de duplicação de código você pode ter pensado:

“Posso separar o código comum entre essas duas páginas e colocar em um único lugar e as diferentes páginas devem referenciar essa implementação.”

Esse pensamento não está de todo errado, mas pode acabar esbarando no principio de YAGNI que vai ser explicado mais a frente.

Em via de regra o que não pode ser repetido são as regra de negócio, os códigos podem ser parecidos e podem estar em contextos diferentes e como tal evoluírem distintamente.

O princípio DRY é uma abordagem eficaz para a redução da complexidade do código, já que quanto menos duplicação de código existe em um sistema, mais fácil é entender e manter o software.

A duplicação de código pode ocorrer de várias maneiras em um sistema. Por exemplo, é comum que os desenvolvedores copiem e colem trechos de código de uma parte do sistema para outra, em vez de criar uma função ou classe que possa ser reutilizada. Isso pode levar a erros, redundância e dificuldades na manutenção e evolução do software.

Para aplicar o princípio DRY no dia a dia do desenvolvimento, algumas técnicas recomendadas incluem:

  1. Usar funções e classes reutilizáveis;
  2. Isolar as funcionalidades em módulos separados;
  3. Utilizar bibliotecas e frameworks de terceiros para evitar a reescrita de código já existente;
  4. Centralizar valores reutilizados em constantes nomeadas (como mensagens de erro, configurações e textos fixos), evitando espalhar valores duplicados pelo código;

KISS ("Keep It Simple, Stupid")

Mantenha isso estupidamente simples!

Incentiva a simplicidade e clareza na concepção e implementação de sistemas. O princípio KISS afirma que a complexidade desnecessária deve ser evitada sempre que possível, e que os sistemas devem ser projetados de maneira simples e direta, para que sejam fáceis de entender, manter e evoluir.

Em outras palavras, significa que um sistema deve ser implementado com simplicidade em mente, evitando o uso de recursos desnecessários, complexidade excessiva e soluções complicadas para problemas simples. Isso pode incluir o uso de uma arquitetura de software simples, padrões de codificação simples, interfaces de usuário simples e documentação clara e objetiva.

Esse princípio pode e deve ser aplicado a todas as fases do processo de desenvolvimento, incluindo análise de requisitos, design, codificação, testes e documentação. Os benefícios do seu uso incluem a redução da complexidade, o aumento da produtividade, a redução do tempo de desenvolvimento e melhoria da qualidade do software.

Algumas práticas ajudam a manter seu sistema simples e claro:

  1. Resolva o problema da forma mais direta e clara possível, sem antecipar abstrações;
  2. Manter o design simples e direto, evitando a complexidade desnecessária;
  3. Evitar o uso de soluções complicadas para problemas simples;
  4. Manter as interfaces de usuário simples e intuitivas;
  5. Escrever código claro e legível, utilizando padrões de codificação simples e consistentes;
  6. Minimizar o uso de dependências e bibliotecas externas, a menos que sejam absolutamente necessárias;
  7. Focar nas funcionalidades mais importantes e essenciais, evitando recursos excessivos ou pouco utilizados;
  8. Por ultimo, verifique se algo ainda pode ser removido do seu código.

Via de regra o primeiro código criado não está simples, então lapide e refatore quantas vezes forem necessários até ele ficar “sem graça”, ou seja fazendo exatamente o que qualquer pessoa que se depare com ele ache que ele deve fazer.

Código simples é um código fácil de entender e manter. Ser mais enxuto e moderno não significa, necessariamente, ser mais limpo e simples. A clareza é mais importante do que todos os outros interesses. Prefira um código verboso, mas fácil de entender e modificar, em vez de um código enxuto que seja difícil de refatorar.

Resolva o problema da forma mais simples possível. Design patterns, novos recursos da linguagem e bibliotecas desnecessárias para a implementação no momento podem tornar o código mais complexo do que o necessário!

YAGNI ("You Ain't Gonna Need It")

Você não vai precisar disso no futuro!

Sugere que não se deve implementar recursos ou funcionalidades que não são essenciais ou não foram solicitados, mesmo que possam ser úteis no futuro. É importante lembrar que, os requisitos e necessidades dos usuários podem mudar ao longo do tempo e, portanto, a criação de funcionalidades que não são obrigatórias pode levar a desperdício de tempo e esforços.

Um exemplo prático seria o desenvolvimento de um aplicativo de TODO, onde o objetivo inicial é criar uma aplicação que permita que o usuário adicione tarefas, marque como concluídas e exclua tarefas da lista. Ao longo do desenvolvimento, pode ser tentador adicionar recursos adicionais, como a capacidade de compartilhar tarefas com outros usuários, adicionar lembretes de data e hora para tarefas específicas, ou adicionar um sistema de recompensas para incentivar os usuários a concluir tarefas.

Embora todas essas ideias sejam ótimas, o princípio YAGNI sugere que esses recursos só devem ser implementados quando forem solicitados pelo usuário ou quando houver um requisito específico para isso.

Otimizações prematuras

A tentativa de melhorar o desempenho antes mesmo de existir um problema concreto pode ser um erro estratégico. Otimizações prematuras podem levar a um código mais complexo, difícil de manter e, muitas vezes, desnecessário. Isso costuma ocorrer quando tentamos prever gargalos antes de validá-los com dados reais.

Por exemplo, escolher uma arquitetura mais complexa para evitar um possível problema de desempenho que ainda não se manifestou pode comprometer a clareza e a simplicidade do sistema como um todo.

Lembre-se: primeiro funcione, depois otimize. Escreva um código claro, funcional e compreensível antes de buscar ajustes finos de performance que podem nunca ser necessários.

Automações desnecessárias

Automatizar tarefas é ótimo, desde que faça sentido para o contexto. Automatizações prematuras ou excessivamente elaboradas podem desperdiçar tempo e esforço que seriam melhor investidos em funcionalidades essenciais.

Exemplos comuns incluem:

  • Ferramentas de análise de código configuradas com regras muito complexas para projetos pequenos e simples.
  • Testes E2E detalhados para funcionalidades triviais, que têm pouco valor frente ao esforço de implementação e manutenção.
  • Automação de tarefas manuais que são raramente executadas e podem ser feitas rapidamente de forma manual sem prejuízo.

Automatize apenas aquilo que é repetitivo, de alto volume ou crítico o suficiente para justificar o investimento. O resto pode (e talvez deva) ser feito de forma mais simples.

Você pode adotar as seguintes estratégias para evitar funcionalidades desnecessárias:

  1. Foco nas funcionalidades essenciais: Concentre-se nas funcionalidades que são essenciais para atender às necessidades do usuário;
  2. Priorização de requisitos: Priorize os requisitos com base na sua importância e valor, garantindo que as funcionalidades essenciais sejam implementadas primeiro e que os recursos sejam usados de forma mais eficiente;
  3. Desenvolvimento iterativo: Permite que as funcionalidades sejam implementadas gradualmente, com base em feedbacks. Isso ajuda a garantir que o desenvolvimento seja orientado pelas necessidades reais do usuário;

POLS ("Principle Of Least Surprise")

Seu código tem que ser chato!

O princípio da menor quantidade de “que merd@ é essa” por minuto. Motiva a previsibilidade do software, de forma que o comportamento de um componente ou função seja intuitivo e fácil de usar, seguindo padrões e convenções conhecidas.

Pode ser alcançado usando nomes de variáveis claros e descritivos e seguindo convenções estabelecidas.

Evitar situações em que o usuário é surpreendido por um comportamento inesperado do software, o que pode levar a confusão, frustração e erros.

  • Front-end

    Sugere que as interfaces de usuário devem ser projetadas para minimizar a surpresa ou confusão do usuário.

    Um exemplo prático é a criação de um botão de "Cancelar" em uma caixa de diálogo que permite que o usuário confirme uma ação. É esperado que um botão "Cancelar" permita que eles saiam da caixa de diálogo sem realizar a ação, se fizesse algo diferente, como confirmar, isso seria uma surpresa ruim.

    Outro exemplo é o uso de ícones para representar ações. Se um ícone é usado para representar uma ação específica em um software, ele deve ser claramente identificável e facilmente reconhecível pelos usuários. Se o ícone não for claro ou não fizer sentido para o usuário, isso pode levar a confusão.

  • Back-end

    Incentiva a criação de APIs, funções e estruturas de dados previsíveis e consistentes. O objetivo é evitar surpresas tanto para quem consome o serviço quanto para quem dá manutenção no código.

    Um exemplo prático é garantir que os endpoints de uma API sigam um padrão consistente de nomenclatura e comportamento. Por exemplo, um endpoint DELETE /users/:id deve realmente remover o usuário com aquele ID, e não apenas desativá-lo silenciosamente ou retornar um erro genérico.

    Outro exemplo é manter o padrão de respostas HTTP. Se uma operação falhar por causa de um dado inválido, espera-se um código 400, e não um 500. Retornar o status correto ajuda a criar previsibilidade no consumo da API.

O princípio POLS é particularmente importante para a usabilidade e acessibilidade do software, pois ajuda a garantir que os usuários possam usar o software de forma eficaz e eficiente, sem se distrair ou confundir com o design da interface do usuário.

CoC ("Convention over Configuration")

Defina convenções e regras em seus projetos!

Promove a padronização do código e do design, de forma que as convenções estabelecidas sejam utilizadas sempre que possível, para evitar repetição de código e reduzir o tempo de desenvolvimento. Os desenvolvedores devem seguir as convenções de codificação estabelecidas na empresa ou comunidade para garantir que o código seja legível e fácil de entender.

  • Ao projetar um sistema, deve-se escolher convenções e padrões bem definidos para reduzir a necessidade de configurações e ajustes manuais.
  • Ao seguir convenções e padrões estabelecidos, a configuração e a personalização do sistema se tornam mais simples e automáticas.

Um exemplo prático do princípio é o framework NestJS. Ao usa-lo, os desenvolvedores podem se concentrar na criação de recursos e funcionalidades do sistema, enquanto o framework lida automaticamente com a configuração e a personalização com base em convenções estabelecidas.

Outro exemplo é o uso de nomes de classes e arquivos consistentes em um projeto de software. Ao seguir convenções estabelecidas para nomear classes e arquivos, os desenvolvedores podem criar estruturas de código mais simples e fáceis de entender, reduzindo a necessidade de configurações manuais.

O princípio CoC é particularmente importante para projetos de software de grande escala, onde a configuração manual de cada componente pode se tornar um grande obstáculo para o desenvolvimento. Ao seguir convenções estabelecidas, os desenvolvedores podem economizar tempo e recursos, permitindo que eles se concentrem em tarefas mais importantes.

Bônus: Object Calisthenics

Para quem deseja ir além dos princípios clássicos, o Object Calisthenics propõe nove regras práticas que incentivam disciplina, coesão e desacoplamento no código orientado a objetos. Ao aplicar essas regras no dia a dia, desenvolvedores fortalecem a qualidade do software e facilitam sua evolução contínua.

O termo “calistenia” remete a exercícios de repetição para fortalecer músculos; aqui, a ideia é fortalecer a disciplina na escrita de código limpo, coeso e desacoplado.

  1. Uma nível de indentação por método: Métodos devem ter apenas um nível de indentação, favorecendo simplicidade e clareza.
  2. Não use else: Prefira polimorfismo ou early return para evitar estruturas complexas de controle.
  3. Envolva primitivos e strings: Crie objetos para representar conceitos do domínio, evitando o uso direto de tipos primitivos.
  4. Coleções de primeira classe: Encapsule coleções em classes próprias, ao invés de expor listas ou arrays diretamente.
  5. Uma linha por declaração: Cada declaração deve ocupar uma linha, aumentando a legibilidade.
  6. Não abrevie: Use nomes descritivos para variáveis, métodos e classes.
  7. Mantenha rodas as classes pequenas: Classes devem ser enxutas e focadas em uma única responsabilidade.
  8. Sem getters/setters/properties: Prefira expor comportamentos ao invés de estados, reforçando o encapsulamento.
  9. Sem múltiplas classes por arquivo: Um arquivo, uma classe, para facilitar manutenção e navegação.

Conclusão

Ao unir os cinco princípios fundamentais (DRY, KISS, YAGNI, POLS, CoC) ao Object Calisthenics, você cria um guia ainda mais completo para o desenvolvimento de software de qualidade. Lembre-se: princípios são meios para atingir um código consistente, não um fim em si mesmos. Busque sempre o equilíbrio entre teoria e prática, adaptando cada conceito à realidade e aos desafios do seu projeto.

Carregando publicação patrocinada...
2

Meus 2 cents,

Confesso que estou bastante dividido aqui: os conceitos sao interessantes, mas juntou elementos "informais" com pontos mais formais - ficou meio esquisito, mas beleza.

Porem acredito que o post precisa de um pouco de revisao - a IA fez um trabalho razoavel de traducao, mas esta traducao/adaptacao de diversos pontos poderiam ser melhor trabalhados, p.ex. nos titulos:

DRY ("Don't Repeat Yourself") - Não repita-se!
KISS ("Keep It Simple, Stupid") - Mantenha isso estupidamente simples!
YAGNI ("You Ain't Gonna Need It") - Você não vai precisar disso no futuro!
POLS ("Principle Of Least Surprise") - Seu código tem que ser chato!

Eh um exemplo - no proprio texto tem diversos outros momentos que a traducao ficou bem pouco natural ou ate mesmo discordante do que quer dizer.

Veja: "You Ain't Gonna Need It" traduzido como "Você não vai precisar disso no futuro!" - vai um tanto contra a ideia real: nao eh que voce nao vai precisar disso no futuro (imperativo) mas que nao se deve implementar hoje algo que ainda nao eh necessario. Tudo bem que no desenvolvimento do texto isso fica melhor explicado - mas como comentei eh uma questao de "polir" algumas traducoes/adaptacoes para o todo ficar mais coeso.

Em "Keep It Simple, Stupid" - "Mantenha isso estupidamente simples!", a traducao perdeu um pouco da forca original, que seria: "Mantenha isso simples, sua besta quadrada" - cabe aqui lembrar que eh um termo anedotico apesar da ideia central ser real. Eh o tipo de situacao que provavelmente a IA quis pegar leve e ser politicamente correta - mas "lost in translation".

Enfim - um post muito legal, mas dar uma polida o tornaria referencia !

1

Fico feliz que tenha achado o post interessante e concordo que com uma revisão pode ser melhorado.
Sobre sua observação "informais" X "formais", fiquei curioso para entender melhor quais trechos você considerou em cada categoria. Se puder apontar exemplos específicos, seria ótimo.
Quanto às traduções dos princípios, a intenção nunca foi fazer uma versão literal dos termos em inglês. Procurei adaptar o significado para o nosso contexto, quase como aquele dilema entre o "anjo e o diabo" no ombro: enquanto o diabo sugere implementar uma nova feature imediatamente, o anjo lembra, "você não vai precisar disso no futuro!". No caso do KISS realmente tem um tom provocativo no original, mas preferi suavizar para quem não está acostumado com esse tipo de humor técnico.
Sobre o YAGNI, concordo totalmente: o foco não é prever o futuro, mas evitar implementar funcionalidades antes da hora. A explicação logo abaixo foi justamente para garantir que o conceito fosse entendido, mesmo com uma tradução menos literal.
Obrigado pelo feedback.

2

A questao de formal e informal que adotei basicamente eh:

Formal: possui um estrutura mais definida, regras ou alguma fundamentacao teorica.

Informal: Sao principios ou enunciados expressados de maneira muito coloquial ou subjetiva

KISS, p.ex., informal, uma vez que tem uma origem anedotica/humoristica e uma ideia central, mas sem uma fundamentacao teorica ou estruturada que possa servir para guiar para sua aplicacao e analise de situacoes, tornando seu uso puramente subjetivo.

Object Calisthenics, por sua vez, tem uma estrutura e regras conforme definido por Jeff Bay (The ThoughtWorks Anthology)

Claro que taxar de formal/informal pode ser tornar uma linha tenue e geralmente cinza - mas o que desejei colocar foi a diferenca entre algo que tem uma estrutura (como Object Calisthenics) vs algo muito subjetivo (como KISS).

DRY ("Don't Repeat Yourself"), apesar de poder ser considerado formal (The Pragmatic Programmer), tem diversos nuances em sua aplicacao (que dependem do contexo).

YAGNI ("You Ain't Gonna Need It") - ja tem um forte componente informal, uma vez que depende de uma analise predominantemente subjetiva (irmao do KISS ?)

POLS ("Principle Of Least Surprise"), tambem tem um forte componente informal (no sentido subjetivo: o que eh uma surpresa ?, como mensurar uma surpresa ?), apesar de influenciar de forma mais tecnica o codigo gerado (como os padroes de REST API)

Reforco - post muito legal.

1