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

  1. ¡Tiene menos bugs!
  2. Es más fácil de usar en pruebas unitarias
  3. 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