Executando verificação de segurança...
6

Clean Code

A um tempo atrás fiz uma paletra sobre clean code, e compartilharei aqui minhas anotações e uns exemplos de código (Os códigos foram todos feitos em TypeScript).

Regras gerais

  • Siga as convenções padrões;
  • Use ferramentas para corrigir e definir as regras do código.
  • Regra dos escoteiros:
    • Deixe o acampamento mais limpo do que o encontrado.

Dicas de compreensão

  • Trabalhe sempre com uma fonte de verdade.
// src/constants/routes.ts
const ROUTES = {
  ROOT: '/',
  LOGIN: '/login',
  // ...
}

export default ROUTES

// src/pages/login.ts
// ...
import ROUTES from '@/constants/routes' 
// ...

navigate(ROUTES.LOGIN)
// ...
  • Absorva ao máximo da linguagem e de arquiteturas utilizadas.
// if/else
const onKeyPress = (event: KeyboardEvent) => {
  if (event.key === 'Enter') {
    //...
  } else if (event.key === 'Backspace') {
    //...
  }

  //...
}

// switch/case
const onKeyPress = (event: KeyboardEvent) => {
  switch (event.key) {
    case 'Enter':
      //...
      break;

    case 'Backspace':
      //...
      break;

    //...

    default:
      //...
      break;
  }
}

O código acima a medida que a aplicação cresce, será gerada cada vez mais validações e ações. Podemos simplificar desta forma:

const navigationHandlers = {
  Enter: (event: KeyboardEvent) => {
    /**/
  },
  Backspace: (event: KeyboardEvent) => {
    /**/
  },
  //...
}

const onKeyPress = (event: KeyboardEvent) => {
  navigationHandlers[key]?.();
}

Desta forma podemos dividir as resposabilidades do código em módulos.

Nomes são importantes

O nome de uma variável, função ou classe deve responder a todas as grandes questões. Deve dizer por que existe, o que faz e como é usado.
Se um nome exigir um comentário, ele não revela sua intenção.

setTimeout(() => console.log('DORIME'), 120000);

O valor de 120.000 milisegundos não deixa claro em quanto tempo a função será chamada.

// Clean
const TWELVE_SECONDS = 12_000;

setTimeout(() => {
  // ...
}, TWELVE_SECONDS);

Não abrevie nomes de variáveis

Não abrevie e evite siglas, exceto se todos conhecerem. (Exemplo: API)

const locations = ['Belo Horizonte', 'Manaus', 'Rio de Janeiro'];
locations.forEach((l) => {
  //...
  dispatch(l);
});

// Clean
const locations = ['Manaus', 'Belo Horizonte', 'Rio de Janeiro'];
locations.forEach((location) => {
  //...
  dispatch(location);
});

A variável "e" e "r" além de estarem abreviada, não é passível de busca, pois uma letra sozinha, compõem diversos outros nomes…

getRepositories()
  .then(res => /**/)
  .catch(e => /**/);
//
new Promise((res, rej) => {});
// Clean
getRepositories()
  .then(response => /**/)
  .catch(error => /**/);
//
new Promise((resolve, reject) => {});

Ao passar mais de um parâmetro em uma função receba os parâmetros dentro de um objeto.

export const createModal = (title, body, onConfirm, onCancel) => {/**/};
//...
createModal(
  'Baby Shark',
  'doo doo doo doo doo doo doo doo',
  () => {},
  () => {}
)

type CreateModal = {
  title: string
  body: string
  onConfirm: () => void
  onCancel: () => void
}

export const createModal = ({
  title,
  body,
  onConfirm,
  onCancel
}: CreateModal) => {
  //...
}

//...

createModal({
  title: 'Baby Shark',
  body: 'doo doo doo doo doo doo doo doo',
  onConfirm: () => {},
  onCancel: () => {},
});

Funções objetivas e pequenas

As funções devem fazer apenas uma coisa, nunca ultrapasse as 20 linhas. No cenário ideal, cada função tem 2, 3 ou 4 linhas e possui uma obviedade transparente. Cada uma conta uma história e leva você a próxima em uma ordem.

const login = ({ user, password }) => {
  if (/* Validação se o usuário é inválido */) {/*...*/}
  if (/* Validação se a senha é inválida */) {/*...*/}
  //...
}
// Clean
const invalidUser = user => {/*...*/}

const invalidPassword = password => {/*...*/}

const login = ({ user, password }) => {
  if (invalidUser(user)) {/*...*/}
  if (invalidPassword(password)) {/*...*/}
  //...
}

Caso seja possível use ternário

const letsWork = (haveCoffe: Boolean): void => {
  if (haveCoffe) {
    console.log('Yep');
  } else {
    console.error("It's not going to be possible");
  }
};

const letsWork = (haveCoffe: Boolean): void => {
  if (haveCoffe) {
    console.log('Yep');
    return
  }

  console.error("It's not going to be possible");
};
// Clean
const letsWork = (haveCoffe: Boolean): void => {
  haveCoffe
    ? console.log('Yep')
    : console.error("It's not going to be possible")
};

Não seja redundante

const itIsOfAge = (age: number): Boolean => (age > 18 ? true : false);
const itIsOfAge = (age: number): Boolean => age > 18;

Escreva seus regex em uma constante

if (/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=*[@$!%*?&])[A-z\d@$!%*?&]{8,}$/.test(password)) {
  //...
}

const passwordIsValid = (password: number): Boolean => {
  // Minimum eight characters
  // At least one uppercase letter
  // One lowercase letter
  // One number
  // One special character

  const validPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=*[@$!%*?&])[A-z\d@$!%*?&]{8,}$/
  return validPassword.test(password);
}

Use sempre as novidades do EcmaScript

const locations: String[] = ['Belo Horizonte', 'Manaus', 'Rio de Janeiro'];
for (let index = 0, index < locations.length; index++) {
  const element = locations[index];
  //...
}

const locations: String[] = ['Belo Horizonte', 'Manaus', 'Rio de Janeiro'];
locations.forEach((location: String) => {
  //...
})

REST/Spread

<Component a="1" {...props} />
<Component {...props, a: 1} />
<Component {...props} a={1} />
const Component = ({ className, ...props }) => {
  const others = { ...props };
  delete others.className;

  return (
    <div className={className}>
      <OtherComponent {...props} />
    </div>
  )
}

const Component = ({ className, ...others }) => (
  <div className={className}>
    <OtherComponent {...others} />
  </div>
);

Defina valores padrões

const Icon = ({ className }) => {
  const additionalClasses = className || 'icon-large'
  return <i className={`icon-hover ${additionalClasses}`} />
};

//
const Icon = ({ className = 'icon-large' }) => (
  <i className={`icon-hover ${className}`} />
);

//
const Icon = ({ className }) => (
  <i className={`icon-hover ${className}`} />
);

Icon.defaultProps = {
  className: 'icon-large',
};

//
class Icon extends React.Component {
  static defaultProps = {
    className: 'icon-large',
  }

  //...

  render() {
    return <i className={`icon-hover ${className}`} />
  }
}

Array destructuring

const splitLocale = navigator.language.split('-')
const language = splitLocale[0]
const country = splitLocale[1]

// Usando array destructuring
const [language, country] = navigator.language.split('-')

// Secrets of array destructuring, set default values
const arrayTest = [1, 2]
const [a, d, c = 3] = arrayTest
console.log(a, d, c);

KISS (Keep It Simple, Stupid — Mantenha simples, estúpido)

Mantenha simples e declarativo, mesmo que seja estúpido

  • Mais simples é sempre melhor;
  • Reduza a complexidade o máximo possível.
const handleSave = (event: MouseEvent) => {/*...*/};

const currentDate = new Date();
// ...
const onSaveClick = (event: MouseEvent) => {/*...*/};
// ...

DRY (Don’t Repeat Yourself — Não se repita)

Se você estiver fazendo a mesma coisa em vários locais, consolide o código duplicado.

const Carousel = () => (
  <>
    <button className="left" onClick={() => {/*...*/}}>
      <Icon name="left" />
    </button>

    {/*...*/}

    <button className="right" onClick={() => {/*...*/}}>
      <Icon name="right" />
    </button>
  </>
);

// Reusable
const ArrowButton = ({ direction, onClick }) => (
  <button className={direction} onClick={onClick}>
    <Icon name={`${direction}`} />
  </button>
);

const Carousel = () => (
  <ArrowButton direction="left" onClick={() => {/*...*/}} />
  {/*...*/}
  <ArrowButton direction="right" onClick={() => {/*...*/}} />
);

Esse assunto é bastaaaaaaaaaaaante extenso, esses foram alguns pequenos exemplos.
Vou deixar algumas referências:

Carregando publicação patrocinada...
4

johnatandias obrigado por trazer esse assunto para o TabNews, e também por listar os vídeos da playlist de Clean Code.

Um desejo meu era refazer eles usando uma qualidade de produção maior... ficaria show 🤝

1

Será uma honra rever esse vídeo, esse assunto me interessa muito, estudo a anos sobre ele, para sempre evoluir na construção das minhas aplicações.

Obrigado pelo feedback.

1

Acho que em "Ao passar mais de um parâmetro em uma função receba os parâmetros dentro de um objeto." faltou colocar heading (#, ##).

É algo que faz total sentido. Se tu consegue colocar todos os parâmetros em um objeto, então você você consegue abstrair tudo para um único parâmetro. O 0 parâmetro seria um método, então você teria que ter isso mapeado numa classe.

Uncle Bob fala que o número ideal de parâmetros é 0; 1 é ótimo; 2 tá OK; 3 ou mais é indicativo que algo tá errado hehehe.

1

só que deixar 0 parâmetros na assinatura do método e deixá-lo amarrado a vários atributos da classe também não me parece uma abordagem muito interessante. isso funciona bem com um "toString()". Já se for um método pra somar 2 valores é muito melhor com parâmetros. mas concordo que parâmetros demais é um problema. melhor encapsular em um objeto.

1
1