¡Hola Colombia 🇨🇴!

¿Como estan?

¿Y tu como estas Miguel?

  • Emocionado de estar en Medellin 🚀
  • (Emocionado)^2 de poderles hablar sobre Node 🤓

Y por supuesto absurdamente nervioso 

Tengan piedad 😅 

😅

😅

😅

😅

😅

😅

😅

😅

😅

😅

😅

😅

😅

😅

😅

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

Sindrome 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 los desarrolladores 9.7M. 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 🤯

Gracias wikipedia ❤️

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", "Jeann"];

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

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

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

speakers.forEach(greetSpeaker);

Código mas ordenado y comprensible 🤓

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

// Dentro de una clase X
// Tenemos un método sendEmail 

const speakers = [
    {
        "name": "Tierney",
        "email": "tierneygmail.com"
    },
    {
        "name": "Kat",
        "email": "katgmail.com"
    },
    {
        "name": "Jean",
        "email": "jeangmail.com"
    }
];

// 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;

☢️

☢️

// Dentro de una clase X
// Tenemos un método sendEmail 

const speakers = [
    {
        "name": "Tierney",
        "email": "tierneygmail.com"
    },
    {
        "name": "Kat",
        "email": "katgmail.com"
    },
    {
        "name": "Jean",
        "email": "jeangmail.com"
    }
];

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 mas 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 😅

El anti-patrón es como lidiamos con esto 💡 

Solución mediante funciones 💡

☢️ Espera antes de lanzar promesas ☢️

function uploadFile(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(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(uploadFile(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: '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;
        })
}

// Fuera

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

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: 'aritex-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: '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;
        })
}

// Fuera

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

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: '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);
                    });
                });
            });
    })
}
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 🤯

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 hailArgs(important1, important2, important3, options) {}

Hoy por mi, mañana por ti 💪

Separadas parecen inofensivas 🤔

Pero juntas 😅

function(req,res){
      let start, end = null;
      if(req.query.year && req.query.month){
        start = `${req.query.year}-${req.query.month}-01`;
        end = `${req.query.year}-${req.query.month}-31`;
      }else{
        start = `2017-08-01`;
        end = `2017-08-08`;
      }
      Product.find({}).lean().exec(function(err, products){
        if(err){
          return res.json({ status: 500, message: "Error finding products" });
        }else if(products != null && products.length > 0){
          async.mapSeries(products, function(product, callback){
            product.total = 0;
            product.variation_sales = [];
            Order.find({ "products": { $elemMatch: { "product.id": product._id }}, "order_type": req.query.order_type || 1}, function(err,orders){
              if(err){
                console.log("Error");
                console.log(err);
                callback();
              }else if(orders != null && orders.length > 0){
                async.mapSeries(orders, function(order, callback){
                  async.mapSeries(order.products, function(pr, callback){
                    if(pr.product.id == product._id){
                      async.mapSeries(pr.variations, function(variation, callback){
                        assignItem(product.variation_sales, product.reference_id,
                          variation.color,
                          variation.size,
                          parseInt(variation.quantity)
                        );
                        product.total += (order.order_type == 2) ? (variation.quantity * product.regular_price) : (variation.quantity * product.price);
                        callback();
                      }, function(err, results){
                        callback();
                      });
                    }else{
                      callback();
                    }
                  }, function(err, results){
                    callback();
                  });
                }, function(err, results){
                  product.total_prends = 0;
                  product.variation_sales.map(va=>{
                    product.total_prends += va.quantity;
                  });
                  callback();
                });
              }else{
                product.total_prends = 0;
                product.variation_sales = [];
                callback();
              }
            });
          }, function(err, results){
            products = products.sort(function(a,b){
              if(a.total > b.total){
                return -1;
              }else if(b.total > a.total){
                return 1;
              }else{
                return 0;
              }
            });
            return res.json({ status: 200, products: products });
          });
        }else{
          return res.json({ status: 500, message: "Error finding products" });
        }
      });
    });

A5: DEMO TIME TENTATIVO

Dependencias circulares :tornado:

A

B

Algo dificiles de dectectar :sweat:

Dependencias circulares :tornado:

A

B

C

Despues de 4 horas dando vueltas :sweat: Nuevamente documentacion :muscle: 

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.

Basicamente a.js viene como un objeto vacio {} de exportaciones :explode:

No es un anti-patron del todo :think: 

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? :scream:

SI :explode:

Rompiendo dependencias circulares simples :sir glass:

// Codigo importaciones simple
// Codigo con exportacion al final o al inicio y se rompe la dependencia circular

Evitarlas al completo? Estructura de archivos :folder:

Detectarlas mas facilmente?

Programa aqui :rocket:

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 sanitiza 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, pero limitalo 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 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 una comunidad, seguridad en Node, GraphQL, aprende y comparte, no todo se hace solo en el desarrollo.

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".

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 😅

NO han googleado como hacer X para obtener Y 😱

Mientras más se sube, menos oxigeno 😱

¿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 😅😢

☢️ Anti-patrones comunes de Javascript

 

⚠️ Sindrome del importor incluido ☢️

☢️ Common JS anti-patterns

 

⚠️ Impostor syndrome included ☢️

Miguel Bolivar

@darking360

🔥

Copy of deck

By Miguel Alejandro Bolivar Portilla

Copy of deck

  • 645