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:
- Filipe Deschamps - CLEAN CODE #1: Introdução
- Filipe Deschamps - CLEAN CODE #2: O que é código limpo?
- Filipe Deschamps - CLEAN CODE #3: A importância dos nomes
- Filipe Deschamps - CLEAN CODE #4: Softwares bem escritos são Funções bem escritas
- Filipe Deschamps - CLEAN CODE #5: Comentários são mentirosos
- Rodrigo Branas - Clean Code with Robert C. Martin