Enlace de la presentación / Slides link:

https://slides.com/darking360/deck-1

☢️ Anti-patrones comunes de Javascript

 

⚠️ Síndrome del impostor incluido

☢️ Common JS anti-patterns

 

⚠️ Impostor syndrome included 

Miguel Bolivar

@darking360

🔥

¡Hola Colombia 🇨🇴!

¿Cómo están?

¿Y tú cómo estás Miguel?

  • Emocionado de estar en Medellín 🚀
  • (Emocionado)^2 de poderles hablar sobre Node 🤓

Y por supuesto absurda-mente nervioso 

Miguel Bolivar
🇻🇪

@darking360 

Fullstack developer

😅

Ingeniero en

Informática

... casi 😅

🎉🎉🎉

💖

@apploi

Agenda 📚

Introducción

Anti patrón 1

Anti patrón 2

Anti patrón 3

Anti patrón 4

Anti patrón 5

Menciones especiales

Síndrome del impostor 🙈

¿Por qué? 🤔

Cierre 😢

A lo que venimos

❤️

❤️

❤️

❤️

❤️

❤️

❤️

❤️

❤️

❤️

Lenguaje

Fenómeno 🤯

¿De frontend a backend así de fácil? 🤔 

JavaScript es el lenguaje de programación más popular, utilizado por 9.7M desarrolladores. Eso es 2.4M de desarrolladores más que el siguiente lenguaje más popular.
 

Primer día: Me voy a comer el mundo 😎

... y así pasa, lo hacen 💅

La pared 💥 

¿Como evitamos esto?

Mejores desarrolladores

Mediante cosas que no sabíamos antes. Conocimiento, pero no cualquier clase de conocimiento

Somos antivirus 👾

En mayor o menor medida 😅

 Los antivirus son programas cuyo objetivo es detectar y eliminar virus informáticos, han evolucionado hacia programas más avanzados que además de buscar y detectar virus informáticos consiguen bloquearlos, desinfectar archivos y prevenir una infección de los mismos.

Conocimiento negativo 🤯

A1: Funciones flecha cuando no es necesario 🙄 

El método forEach() ejecuta la función indicada una vez por cada elemento del array.

const speakers = ["Tierney", "Kat", "Kevin"];

speakers.forEach((speaker) => {
    console.log(`Hey ${speaker}! Welcome to Medellin!`);
});
const speakers = ["Tierney", "Kat", "Kevin"];

speakers.forEach(function(speaker) {
    console.log(`Hey ${speaker}! Welcome to Medellin!`);
});
const greetSpeaker = require("utils/greet.js");

const speakers = ["Tierney", "Kat", "Kevin"];

speakers.forEach(greetSpeaker);

Código más ordenado y comprensible 🤓

Pero la vida no es así de fácil 😭

// Dentro de una clase X
// Tenemos un método sendEmail 
// No por favor
// ☢️ const _this = this; ☢️
// NO

speakers.forEach((speaker) => {
    console.log(`Hey ${speaker.name}! Welcome to Medellin!`);
    this.sendEmail(speaker.email)
});

const  _this = this;

☢️

☢️

// Oh boy 🙊

speakers.forEach(function(speaker) {
    console.log(`Hey ${speaker.name}! Welcome to Medellin!`);
    this.sendEmail(speaker.email)
}, this);

Recordatorio: Leer la documentación 😅 

thisArg

Valor que se usará como this cuando se ejecute el callback.

A2: Callback hell 🔥

Se ve más o menos así 🤔

fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

// AIUDA 😱

Y en realidad es hasta peor 😱

function writeAWS(reference, callback){
  fs.readFile(`public/uploads/${reference}.png`, function (err, data) {
    if (err) throw err; // Something went wrong!
    var s3bucket = new AWS.S3({params: {Bucket: 'nodeconf-prod'}});
    s3bucket.createBucket(function () {
        var params = {
            Key: `profiles/${reference}.png`, //file.name doesn't exist as a property
            Body: data,
            ACL: 'public-read'
        };
        s3bucket.upload(params, function (err, data) {
          // Whether there is an error or not, delete the temp file
          fs.unlink(`public/uploads/${reference}.png`, function (err) {
              if (err) {
                  console.error(err);
                  return;
              }
              console.log('Temp File Delete');
          });
          if (err) {
              console.log('ERROR MSG: ', err);
              callback(err);
          } else {
            callback(null, data.Location);
          }
      });
    });
  });
}

// (AIUDA)**2 😱

Miguel del 2017 llorando de fondo 😅

Usualmente un anti-patrón es como lidiamos con esto 💡 

Solución mediante funciones 💡

☢️ Espera antes de lanzar promesas ☢️

function uploadFile(data, reference, callback) {
    var params = {
        Key: `profiles/${reference}.png`,
        Body: data,
        ACL: 'public-read'
    };
    s3bucket.upload(params, function (err, data) {
        fs.unlink(`public/uploads/${reference}.png`, function (err) {
            if (err) {
                console.error(err);
                return;
            }
            console.log('Temp File Delete');
        });
        if (err) {
            console.log('ERROR MSG: ', err);
            callback(err);
        } else {
            callback(null, data.Location);
        }
    });
}

function writeAWS(reference, callback){
  fs.readFile(`public/uploads/${reference}.png`, function (err, data) {
    if (err) throw err; // Something went wrong!
    var s3bucket = new AWS.S3({params: {Bucket: 'nodeconf-prod'}});
      s3bucket.createBucket(uploadFile(reference, callback));
  });
}
function logError(err) {
    if (err) {
        console.error(err);
        return;
    }
    console.log('Temp File Delete');
}

function uploadFile(data, reference, callback) {
    var params = {
        Key: `profiles/${reference}.png`,
        Body: data,
        ACL: 'public-read'
    };
    s3bucket.upload(params, function (err, data) {
        fs.unlink(`public/uploads/${reference}.png`, logError);
        if (err) {
            console.log('ERROR MSG: ', err);
            callback(err);
        } else {
            callback(null, data.Location);
        }
    });
}

function writeAWS(reference, callback){
  fs.readFile(`public/uploads/${reference}.png`, function (err, data) {
    if (err) throw err; // Something went wrong!
    var s3bucket = new AWS.S3({params: {Bucket: 'nodeconf-prod'}});
      s3bucket.createBucket((data) => uploadFile(data, reference, callback));
  });
}

Promesas al rescate 💡 

function writeAWS(reference, callback) {
    return fs.readFile(`public/uploads/${reference}.png`)
        .then(function handleReadFile(data) {
            var s3bucket = new AWS.S3({ params: { Bucket: 'nodeconf-prod' } });
            return s3bucket.createBucket().then(function handleBucket() {
                var params = {
                    Key: `profiles/${reference}.png`, //file.name doesn't exist as a property
                    Body: data,
                    ACL: 'public-read'
                };
                s3bucket.upload(params).then(function (err, data) {
                    // Whether there is an error or not, delete the temp file
                    fs.unlink(`public/uploads/${reference}.png`, function (err) {
                        if (err) {
                            console.error(err);
                        }
                        console.log('Temp File Delete');
                    });
                    if (err) {
                        console.log('ERROR MSG: ', err);
                        callback(err);
                    } else {
                        callback(null, data.Location);
                    }
                });
            });
        })
}

Bueno casi 😅

Promesas legibles 💡 

function writeAWS(reference) {
    return fs.readFile(`public/uploads/${reference}.png`)
        .then((data) => {
            var s3bucket = new AWS.S3({
                params: {
                    Bucket: 'nodeconf-prod'
                }
            });
            return s3bucket.createBucket().then(() => { data, s3bucket });
        })
        .then(({ data, s3bucket }) => {
            var params = {
                Key: `profiles/${reference}.png`,
                Body: data,
                ACL: 'public-read'
            };
            return s3bucket.upload(params);
        })
        .then((data) => {
            // Unlink no matter what
            fs.unlink(`public/uploads/${reference}.png`);
            return data.Location;
        })
}

Ahora si 💪

Simplificando aún mas 🤯

function writeAWS(reference) {
    return readFile(reference)
        .then(createS3Bucket)
        .then(uploadReferenceToS3)
        .then(unlinkAndReturn)
}

// Fuera

writeAWS(ghostReference)
    .then(handleSuccess)
    .error(handleError);

Async/await ❤️ 

async function writeAWS(reference) {
    try {
        const data = await fs.readFile(`public/uploads/${reference}.png`);
        var s3bucket = new AWS.S3({
            params: {
                Bucket: 'nodeconf-prod'
            }
        });
        await s3bucket.createBucket();
        const uploadData = await s3bucket.upload(params);
        // Unlink no matter what
        fs.unlink(`public/uploads/${reference}.png`);
        return uploadData.Location;
    } catch (error) {
        console.log(error);
    }
}

Tienen razón, se ve mejor así 💅

A3: New Promise innecesario 🤯

function writeAWS(reference) {
    return fs.readFile(`public/uploads/${reference}.png`)
        .then((data) => {
            var s3bucket = new AWS.S3({
                params: {
                    Bucket: 'nodeconf-prod'
                }
            });
            return s3bucket.createBucket().then(() => { data, s3bucket });
        })
        .then(({ data, s3bucket }) => {
            var params = {
                Key: `profiles/${reference}.png`,
                Body: data,
                ACL: 'public-read'
            };
            return s3bucket.upload(params);
        })
        .then((data) => {
            // Unlink no matter what
            fs.unlink(`public/uploads/${reference}.png`);
            return data.Location;
        })
}

Por si acaso 😅

function writeAWS(reference) {
    return new Promise(function (resolve, reject) {
        fs.readFile(`public/uploads/${reference}.png`)
            .then((data) => {
                var s3bucket = new AWS.S3({
                    params: {
                        Bucket: 'nodeconf-prod'
                    }
                });
                s3bucket.createBucket().then(() => {
                    var params = {
                        Key: `profiles/${reference}.png`,
                        Body: data,
                        ACL: 'public-read'
                    };
                    s3bucket.upload(params).then(() => {
                         // Unlink no matter what
                         fs.unlink(`public/uploads/${reference}.png`);
                         resolve(data.Location);
                    });
                });
            });
    })
}
function writeAWS(reference) {
    return new Promise(function (resolve, reject) {
        fs.readFile(`public/uploads/${reference}.png`)
            .then((data) => {
                var s3bucket = new AWS.S3({
                    params: {
                        Bucket: 'aritex-prod'
                    }
                });
                s3bucket.createBucket().then(() => {
                    var params = {
                        Key: `profiles/${reference}.png`,
                        Body: data,
                        ACL: 'public-read'
                    };
                    s3bucket.upload(params).then(() => {
                        // Unlink no matter what
                        fs.unlink(`public/uploads/${reference}.png`);
                        resolve(data.Location);
                    }).catch(err => reject(err));
                }).catch(err => reject(err));
            }).catch(err => reject(err));
    })
}

Encadenamiento de promesas al rescate🦸 

Una necesidad común es el ejecutar dos o más operaciones asíncronas seguidas, donde cada operación posterior se inicia cuando la operación previa tiene éxito, con el resultado del paso previo. Logramos esto creando una cadena de promesas.

// Por favor no 😅
hazAlgo(function(resultado) {
  hazAlgoMas(resultado, function(nuevoResultado) {
    hazLaTerceraCosa(nuevoResultado, function(resultadoFinal) {
      console.log(resultadoFinal);
    }, falloCallback);
  }, falloCallback);
}, falloCallback);

// Hecho de manera cool 😎
hazAlgo().then(function(resultado) {
  return hazAlgoMas(resultado);
})
.then(function(nuevoResultado) {
  return hazLaTerceraCosa(nuevoResultado);
})
.then(function(resultadoFinal) {
  console.log(resultadoFinal);
})
.catch(falloCallback);

Un poco de trampa 😂

function writeAWS(reference) {
    return fs.readFile(`public/uploads/${reference}.png`)
        .then((data) => {
            var s3bucket = new AWS.S3({
                params: {
                    Bucket: 'aritex-prod'
                }
            });
            return s3bucket.createBucket().then(() => { data, s3bucket });
        })
        .then(({ data, s3bucket }) => {
            var params = {
                Key: `profiles/${reference}.png`,
                Body: data,
                ACL: 'public-read'
            };
            return s3bucket.upload(params);
        })
        .then((data) => {
            // Unlink no matter what
            fs.unlink(`public/uploads/${reference}.png`);
            return data.Location;
        })
}

A4: Complejidad, longitud y argumentos 😕 

Empecemos por la complejidad ciclomatica 🤓

Es el conteo de las rutas lineal-mente independientes a través del código fuente. También se puede pensar en esto simplemente como "la cantidad de decisiones que debe tomar un bloque de código dado".

Viéndolo un poco mas en código 😅

const valor = 10;
const valores = [1,2,3,4,5]
let final;
if (valor > 5) {
    final = 20;
} else if (valor <= 5) {
    final = 30;
}
for (var i = 0 ; i < valores.length ; i++) {
    if (valor > 5) {
        final += 20;
    } else if (valor <= 5) {
        final += 30;
    }
}
if (final > 25) {
    console.log("Aiuda");
}

Puntos de decisión: for, while, if, switch, try/catch 🤔

Ahora complejidad cognitiva 🤯

Es una medida de lo difícil que es entender intuitivamente una unidad de código. Te dice lo difícil que será leer y entender tu código.

Haciendo trampa de nuevo 😅

const valor = 10;
const valores = [1,2,3,4,5]
let final;
if (valor > 5 && valor < 25) {
    final = 20;
} else if (valor <= 5 && valor > 0 && valor % 2 === 0) {
    final = 30;
}
for (var i = 0 ; i < valores.length ; i++) {
    if (valor > 5 || true) {
        final += 20;
    } else if (valor <= 5) {
        final += final % 5 === 0 ? 5 : 0;
    }
}
if (final > 25) {
    console.log("Aiuda");
}

Puntos de decisión: for, while, if, switch, try/catch, &&, ||, recursividad y ternarios 🤯

Longitud 🕵️

Funciones de máximo 25 o 30 lineas 📋

Mas fáciles de depurar y probar 🤓

Evitar 54652 argumentos 😱

function hailArgs(arg1, arg2, arg3 arg4 arg5, arg6, arg7, arg8) {}

// Se chevere 😎

function lessArgs(important1, important2, important3, options) {}

Hoy por mi, mañana por ti 💪

Evitarlas más fácil 💡

ESLint 🤓

{
    "rules": {
        "complexity": ["error", 5],
        "max-params": ["error", 4],
        "max-lines-per-function": ["error", 25]
    }
}

A5: Micro Demo TIME 👀

A5: Dependencias circulares 🌪️

A

B

Algo "difíciles" de detectar 😅

Dependencias circulares 🌪️

A

B

C

Después de 4 horas dando vueltas 😅 Nuevamente documentación 💪

Cuando main.js carga a.js, entonces a.js a su vez carga b.js. En ese punto, b.js intenta cargar a.js. Para evitar un bucle infinito, una copia inacabada del objeto de exportaciones a.js se devuelve al módulo b.js. b.js luego termina de cargarse, y su objeto de exportación se proporciona al módulo a.js.

Básicamente a.js viene como un objeto vació {} de exportaciones 🤯

No es un anti-patrón del todo 🤔 

Se requiere una planificación cuidadosa para permitir que las dependencias de los módulos cíclicos funcionen correctamente dentro de una aplicación.

Se pueden permitir? 😱

SI 🤯

Rompiendo dependencias circulares simples 🥃

Agregamos el objeto de exportaciones al inicio del archivo para romper la dependencia 💣 y vamos agregando las exportaciones a este objeto 👌

var exportsObject = {};

module.exports = exportsObject;

// Super cool and messy code 😎

exportsObject.messyFunction = function(args1) {
    console.log("Nice 👍");
}

Evitarlas al completo? Estructura de archivos 📁

Detectarlas más fácilmente?

Madge al rescate 🚀

🎉 Sobrevivimos 🎊

El demo no fue planeado con meses de antelación 😅

Menciones especiales 🏆

No entraban todos 😅

1.- Utilizar .then(sucess, fail), es mejor un .catch() que englobe a las excepciones de una cadena de promesas.

2.- Usar nombres no claros en variables: const { _xlr } = options; ← Qué🤔

3.- Pasar queries puros lleva a inyecciones (MongoDB, MySQL, etc). Utiliza un ORM o depura las consultas 🤓

4.- Sin límites, desde número de solicitudes permitidos por un endpoint, hasta intentos de login o acción en un endpoint por usuario, tu programa funciona, usa restricciones para que no abusen de el.

6.- Nunca actualizar paquetes es fatal, a medida que pasa el tiempo se descubren issues de seguridad en versiones de Node o librerías, por favor actualiza. npm audit nos ayuda con esto, y se inventó por algo (Hola Kat ❤️).

7.- El programa principal hace todo. Existen los workers threads, y tienen toda una piscina de hilos donde trabajar 🏊  aprende a delegar, crea sistemas de encolado, y que las tareas que puedan bloquear el hilo principal de ejecución vayan a workers que se encarguen de esto 💡

8.- No monitorear. Debemos loguear todo en nuestro sistema: transacciones, conversiones, errores, y tenerlos a la mano en una herramienta de monitoreo, y que estas generen alertas.

9.- Node solo puede con todo. No, utiliza un gestor de procesos como PM2 que se encargue de gestionar tu aplicación, su hilo principal y sus workers.

10.- Yo solo puedo 💅 ni de cerca. Acércate a comunidades, aprende y comparte, no todo se hace solo en el desarrollo.

Fuentes varias 🤓

Síndrome del impostor 🙈

Es un fenómeno psicológico en el que la gente es incapaz de internalizar sus logros y sufre un miedo persistente de ser descubierto como un fraude.

Esta bien 🤯

Ancla ⚓ Manten los pies en la tierra!

Pasa a todo nivel en la industria, no hay salvación ☢️

Llama a la mejora constante 🤓

Estas en una posición importante 😎

Y estas ahí por algo 🤟

El anti-patrón no es tenerlo 😱

Es no saber aprovecharlo 🤔

¿Por qué? 🤔

Conocimiento negativo 💡

La experiencia necesaria en las empresas modernas es difícilmente "lineal", "monolítica" y "cronológica".

Un ejercicio 🏋️‍♂️

Levanten la mano si

Han googleado como hacer X para obtener Y 😅

Han googleado como NO hacer X para obtener Y 😱

Mientras más se sube, menos oxígeno 😱

¿Como escalar un sistema de micro servicios basados en estrella? 🙃

Historias de fracaso escalando micro servicios ✔️

Soluciones 👍

Fracaso, errores, anti-patrones, problemas ✔️ ☢️ 🤯

Cierre 😭

Les mentí 😅

Quizás nos quedemos en el mismo punto 😒 

Esto no es un silver bullet 😢

Pero les aseguro que sabremos a donde NO tenemos que ir o hacer 🤔

Formas de hacer las cosas bien, pocas 😱

Formas de hacer las cosas terriblemente mal, muchísimas, y muy fáciles de caer en ellas 😅😢

Miguel Bolivar

@darking360

🔥

¡Muchisimas gracias! 🙏

Son lo máximo 🤘

☢️ Common JS anti-patterns ⚠️ Impostor syndrome included ☣️

By Miguel Alejandro Bolivar Portilla

☢️ Common JS anti-patterns ⚠️ Impostor syndrome included ☣️

NodeConf Colombia 2019 talk slides - Spanish.

  • 1,292