Princípio KISS (Keep it Simple Stupid): Como podemos aplicar?

Princípio KISS (Keep it Simple Stupid): Como podemos aplicar?

O simples é muitas vezes suficiente no mundo da Tecnologia

Pode até parecer um termo grosseiro. Veja a tradução livre para a frase:

"Mantenha isso simples e estupido! "

Mas quando estamos falando de manter um software, definindo as tecnologias, sua arquitetura e seu código, é sempre melhor escolher o lado da simplicidade. Vamos entender melhor como este principio pode se encaixar no nosso dia a dia e as vantagens de aplica-lo.

Escolha o caminho mais fácil

Quando um projeto novo é iniciado, as decisões tomadas por todos do time podem ser cruciais para o bom andamento do projeto. Como assim?

Imagine que um cliente acabou de contratar a empresa em que você trabalha para desenvolver a aplicação que eles precisam. Eles determinam prazos realistas, mas como sempre pedem agilidade nas entregas. Sabemos que prazos são importantes e por mais realistas que eles sejam, imprevistos podem ocorrer. A equipe de desenvolvimento tem muita facilidade e conhecimento com um framework (biblioteca) ou linguagem de programação especifica e elas atendem perfeitamente as necessidades para desenvolver a aplicação que seu cliente necessita. Vamos adicionar outro fator aqui. A experiência do time em projetos e tempo na área de desenvolvimento de software não é muito grande. Mas então a equipe decide ir para outros frameworks e linguagens que não estão totalmente adaptados ou que será necessário tempo de estudo para conhecer. Mas desafios novos são sempre bem vindos, não é mesmo? No começo tudo parece ir bem e a equipe parece lidar tranquilamente com as particularidades da linguagem e framework. Depois de um tempo, os desenvolvedores Front-end começam a ter problemas em trabalhar com algo especifico da biblioteca. Os desenvolvedores Back-end começam a quebrar a cabeça para entender o porque alguns tratamento de exceções não funcionam como eles gostariam. As demandas começam a crescer, alguns testes vão ficando para trás, o prazo está no limite e a aplicação começa a ficar uma bagunça. Onde podemos aplicar o KISS nessa historia?

Fica claro que as decisões feitas pelo time não foram as mais simples! A equipe tinha conhecimento em uma linguagem X e framework Y. Mas a decisão final foi adotar tecnologias e linguagens de programação que não estavam acostumados e que nunca tinham aplicado em projetos reais.

Não existe problema nenhum conhecer, estudar e trabalhar com tecnologias novas. Mas o problema da situação foi que já existia um prazo curto para entrega da aplicação. Não era o momento ideal de partirem para algo novo sendo que ninguém do time dominava as tecnologias escolhidas.

Se esse exemplo não ficou claro. Veja outro.

O time de desenvolvimento acabou de receber uma demanda grande. Ao analisar com calma, percebem que existe a necessidade de verificar uma regra de negócio especifica na aplicação e que é essencial para que o usuário consiga usar a aplicação. Alguns membros da equipe começam a consultar empresas que já realizam essa verificação e tem a solução pronta. Mas outros do time dizem:

Não precisamos consumir um serviço (API) de terceiro. Vamos criar um serviço próprio que será responsável por toda as verificações que precisamos e assim podemos economizar dinheiro. Não deve ser tão complexo...

Então em vez de buscarem no mercado uma empresa que já tem a solução pronta, que funciona e que muitas empresas usam, começam a criar uma do zero e alocam alguns desenvolvedores do projeto principal para desenvolverem a arquitetura e lógica do serviço. A equipe toda se dedica e conseguem! Localmente (localhost 😄) tudo funciona muito bem. Começa então a fase de testes e o serviço vai para um ambiente de homologação, para que o QA possa testar seguindo as exigências e cenários que o cliente determinou. Mas acontece o que ninguém imaginava. Algumas verificações funcionam e outras não. Provavelmente os desenvolvedores seguiram uma caminho 'vicioso' ao testarem em suas maquinas. Depois descobrem que com a LGPD já em vigor, precisam fazer adaptações no código e servidores para preservar os dados que serão persistidos. Não existe margem para erro, pois se essa verificação não for feita corretamente seu cliente terá prejuízos altos. O prazo de entrega começa a se esgotar, mas a aplicação principal não está completa. Então é feito todo um trabalho de pesquisa e contratam o serviço de uma empresa terceira que já tem a solução pronta e está em conformidade com a Lei Geral de Proteção de Dados. O projeto é entregue com atraso que poderia ter sido evitado.

Novamente o problema aqui foi não manter as coisas simples! O propósito poderia ter sido economizar, mas não concorda que existem soluções que são acessíveis financeiramente e que funcionam muito bem? Algumas até mesmo são gratuitas dependendo da demanda. Criar uma solução do zero e que realmente funcione pode consumir um tempo que não existe no momento. Principalmente se você não tiver uma equipe grande de desenvolvedores e com sólida experiência.

5iyqnn.jpg

Espero que tenha ficado claro com esses exemplos. Decisões não precisam ser direcionadas para o caminho mais difícil! Não precisamos ter uma arquitetura complexa desde o início e trabalhar com 301 tecnologias diferentes. As aplicações evoluem a partir das demandas e números de usuários que fazem uso dela. Então não adianta trabalhar com uma base de dados não relacional achando que ganhará performance se sua base tem poucos dados armazenados e sua aplicação tem apenas 10 usuários consumindo os dados. Busque por simplicidade!

Mantenha seu código estupidamente simples

Não é difícil encontrar linhas de códigos que poderiam ter sido simplificadas ou ter sido escritas com objetivo. Manter o código legível, simples de entender e fácil de realizar manutenção, deveria ser obrigação de todo o desenvolvedor. Mas sabemos que na prática não é bem assim. Vamos ver alguns exemplos com C# de como manter um código simples e sem inventar. Veja estes exemplos bem simples:

if (PrimeiraCondicao) {
  // ...
} if (SegundaCondicao) { 
  //...
}

Existe alguma necessidade lógica de se colocar a segunda condição dessa maneira? Obviamente não. Isso seria manter o código simples? Também não! Isso pode induzir outros ao erro. O próximo desenvolvedor que for ler pode pensar:

"Será que na verdade aqui deveria ter um else?"

É bom sempre lembrar que o código fica mais claro quando cada instrução tem sua própria linha. O código acima trata-se de condições separadas, mas isso pode não ficar claro com o código organizado dessa maneira! Veja esse mesmo código corrigido:

if (PrimeiraCondicao) 
{
  // ...
}

if (SegundaCondicao) 
{
  //...
}

// Ou usar um else if

if (PrimeiraCondicao)
{
  // ...
} else if (SegundaCondicao) {
  //...
}

Veja outros exemplos agora com expressões booleanas:

public void Exemplo(bool x, bool y)
{
    var z = true;
    if (z)
    {
        FazAlgo();
    }

    if (x && z)
    {
         FazAlgo();
    }

    if (y || !z) // Neste caso Z sempre será falso
    {
       FazAlgo();
    }

Consegue enxergar o problema do código acima? A variável Z sempre é true. Então qual a necessidade de verificar ela? Seria um caso diferente se ela estivesse sendo declarada como argumento dentro de um método (função) na primeira condição. E sendo que ela sempre será true não é necessário verificar junto com X ou Y. Esse código também é um Code Smell. Com certeza não é a melhor maneira de deixar nosso código simples. Veja a correção do código:

public void Exemplo(bool x, bool y)
{
    var z = true;
    if (Metodo(z))
    {
        FazAlgo();
    }

    if (x)
    {
        FazAlgo();
    }

    if (y)
    {
        FazAlgo();
    }

E o que dizer de inverter as expressões booleanas, deixando o código complexo em vez de simples? Veja o código abaixo:

if ( !(x == 5)) { ...}  
bool c = !(i < 2);

O desenvolvedor que pensou no código acima se preocupou em algum momento em deixar o código simples de outros lerem? Com certeza não! Evite colocar complexidade em condições que são simples:

// Código corrigido

if (x != 5) 
{
// ... 
}
bool c = (i >= 2);

Com certeza é mais fácil de entender escrito assim. Agora temos um código simples de se ler!

Vamos ver mais um exemplo em código, agora analisando um caso usando o operador switch :

switch (variavel)
{
  case 0:
    ExecutarTarefa();
    break;
  default:
    ExecutarTarefa();
    break;
}

Aparentemente não tem nada de errado com esse código e não é tão difícil de entender. Na verdade podemos simplificar usando if / else. As instruções e expressões switch são úteis quando há muitos casos diferentes, aqui temos apenas duas condições a serem verificadas. Então podemos substituir e simplificar por este código:

if (variavel == 0)
{
  ExecutarTarefa();
}
else
{
  ExecutarOutraTarefa();
}

O objetivo é manter o código simples!

Conclusão

Desenvolver um software não é uma tarefa simples. Existem várias escolhas a se fazer e elas podem ou não afetar o andamento do projeto no futuro. Aplicar o princípio KISS tanto em decisões de arquitetura e no próprio código pode facilitar muito seu dia a dia. Lembre-se da frase de Martin Fowler no clássico livro Refactoring – Improving the Design of Existing Code:

Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos possam entender

Fico por aqui neste post, agradeço muito por ler até aqui! Até o próximo! 😉