Lo que todo desarrollador
debe saber sobre Código Limpio
¿Qué es
Código Limpio?
Código Limpio
Código que se entiende sin tener al autor, puedo ser yo de hace unos días, al lado
Importancia
del Código Limpio
- ¡Tiene menos bugs!
- Es más fácil de usar en pruebas unitarias
- Es más fácil de extender o modificar
¿Por qué tiene menos bugs?
Responde lo siguiente,
del siguiente código:
- ¿Cuántos posibles resultas hay al llamar la función?
- ¿En qué condiciones la función falla?
- ¿En qué condiciones retorna un valor?
if (StringUtils.isNotBlank(resultStatus)) {
if (resultStatus.equalsIgnoreCase(ERROR)
&& errorCode != null
&& StringUtils.isNotBlank(errorMessage)) {
obj.addProperty(STATUS, FAILURE);
if (errorCode == 361
&& errorMessage.localeCompare("unique_violation")) {
obj.addProperty(STATUS, UPDATE);
} else {
obj.addProperty(STATUS, FAILURE);
throw new ABCException(errorMessage);
}
} else if (resultStatus.localeCompare("ok")) {
obj.addProperty(STATUS, SUCCESS);
} else {
obj.addProperty(STATUS, FAILURE);
throw new ABCException(errorMessage);
}
} else {
obj.addProperty(STATUS, FAILURE);
throw new ABCException(errorMessage);
}
return convertedObject;
¡Es super sencillo que un bug
es esconda en código spaghetti!
¿Qué tan difícil
es escribir
código limpio?
Regla Fácil:
si se ve "raro",
no es muy limpio
Pero también hay algunas reglas, no tan obvias...
1. Muchas funciones
El código más
fácil de leer es el
que tiene
muchas funciones
if (n % 2 == 0) {
// ¿Cuándo se ejectura?
}
¿Cuáles son equivalentes?
if (n % 2 == 1) {}
if (n % 2 != 1) {}
if (n % 1 % == 0) {}
No es lo mismo simple que común
function esPar(n) {
return n % 2 == 0;
}
function esImpar(n) {
return !esPar(n);
}
if (esPar(n)) {
// ¡Nunca hay ambigüedad!
// siempre y cuando sepas leer
}
¡Conozco una manera
más eficiente de saber
si un número es par!
function esPar(n) {
return (n & 1) === 0;
}
Haciendo un solo cambio, todo un proyecto se ve beneficiado
Una función
añade un grado
de indirección
¡Momento!
¡Todos saben qué es estúpido hacer una función de una línea!
¿Qué sucede cuando se invoca a una función?
- El programa tiene que agregar
una función al stack de ejecución - También tiene que hacer una copia
de los argumentos usados en la función
Hay una penalidad en tiempo y espacio
Si, pero...
La legibilidad
es usualmente mucho más importante que el performance
Pero también está
la magia del compilador
Un compilador puede decir
hacer inlining
¿Y qué sucede
si una función solo se usa una vez?
Una función no
tiene nada que ver con reutilización
Una función
tiene TODO que
ver con legibilidad
Una función
da un nombre
a un algoritmo
¿Qué es esto
de escribir código declarativo?
"a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow"
Sieve of Eratosthenes
class Sieve {
constructor(maxPrime) {
this.maxPrime = maxPrime;
createSieve();
uncrossAllNumbers();
crossObviousNumbers();
crossMultiplesOfKnownPrimes();
}
}
2. Funciones Pequeñas
¿Cuántas líneas de Código tiene una función pequeña?
¡Esa es la
pregunta equivocada!
¿Cuántos niveles
de abstracción tiene una función?
Single Level of Abstraction (SLA)
Separar
- El código que inicializa una variable
- El código que utiliza una variable
Separar
- El código que usa los
detalles de implementación
de una clase - El código que provee
el API de esa clase
Separar
- El código que accede al DOM
- El código con la lógica de negocio
Cabe siempre preguntar ¿puede esta función ser dividida al menos en dos?
La infinita superioridad de las funciones pequeñas
Funciones Pequeñas
- Son fáciles de razonar
- Son fáciles de reutilizar
- Son fáciles de probar
3. Retorno rápido
Un lugar común de bugs es el mal manejo de "errores"
Combinar código
que maneja errores
y código que no
es receta para el desastre
La primera parte
de un método
debe probar los estados de "error" y retornar temprano
function ellipsy(text) {
if (text.length < 250) {
return text;
}
const cutIndex = firstSpaceAfter(text, 250);
const shortText = text.substring(0, cutIndex);
return `${shortText} …`;
}
function foo() {
if (condition) {
// code
// code
// code
// code
// code
} else {
// error handling
}
return ...;
}
function foo() {
if (!condition) {
return ...;
}
// code
// code
// code
// code
// code
return ...;
}
Pero un método no debe tener más de un return
La pureza matemática es muchísimo menos importante que evitar los errores
Fail Fast
¿Por qué no validamos parámetros?
function foo(abc, def, ghi) {
if (!abc) {
throw new Error();
}
if (def <= 0) {
throw new RangeError();
}
if (!(ghi instanceof Function)) {
throw new TypeError();
}
// 😞
}
function bar(abc, def, ghi) {
Preconditions.requireNonNull(abc);
Preconditions.requirePositive(def);
Preconditions.requireFunction(ghi);
// 😃
}
function bar(abc, def, ghi) {
checkPreconditions(abc, def, ghi);
// 😃
}
Utiliza una librería
de Preconditions
o haz la tuya
4. NULL / Undefined
4. Optional Chaining
Nullish Coalescing
5. Manejo de Errores
Lo que todo desarrollador debe saber sobre Código Limpio
By Carlos Obregón
Lo que todo desarrollador debe saber sobre Código Limpio
¿Por qué el Código Limpio es tan importante? ¿Cuáles son los principios fundamentales del código limpio?
- 907