Miguel Alejandro Bolivar Portilla
Informatics Engineer. Full-stack developer.
Enlace de la presentación / Slides link:
Miguel Bolivar
@darking360
(Emocionado)^2 de poderles hablar sobre Node 🤓
Y por supuesto absurda-mente nervioso
... casi 😅
@apploi
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 😢
❤️
❤️
❤️
❤️
❤️
❤️
❤️
❤️
❤️
❤️
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.
... y así pasa, lo hacen 💅
Mediante cosas que no sabíamos antes. Conocimiento, pero no cualquier clase de conocimiento
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.
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);
thisArg
Valor que se usará como this cuando se ejecute el callback.
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 😱
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 😅
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));
});
}
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 😅
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 💪
function writeAWS(reference) {
return readFile(reference)
.then(createS3Bucket)
.then(uploadReferenceToS3)
.then(unlinkAndReturn)
}
// Fuera
writeAWS(ghostReference)
.then(handleSuccess)
.error(handleError);
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í 💅
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;
})
}
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));
})
}
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);
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;
})
}
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".
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 🤔
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.
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 🤯
Funciones de máximo 25 o 30 lineas 📋
Mas fáciles de depurar y probar 🤓
function hailArgs(arg1, arg2, arg3 arg4 arg5, arg6, arg7, arg8) {}
// Se chevere 😎
function lessArgs(important1, important2, important3, options) {}
{
"rules": {
"complexity": ["error", 5],
"max-params": ["error", 4],
"max-lines-per-function": ["error", 25]
}
}
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 🤯
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? 😱
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 👍");
}
El demo no fue planeado con meses de antelación 😅
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.
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.
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 🤟
La experiencia necesaria en las empresas modernas es difícilmente "lineal", "monolítica" y "cronológica".
Han googleado como hacer X para obtener Y ✋😅
Han googleado como NO hacer X para obtener Y ✋😱
¿Como escalar un sistema de micro servicios basados en estrella? 🙃
Historias de fracaso escalando micro servicios ✔️
Soluciones 👍
Fracaso, errores, anti-patrones, problemas ✔️ ☢️ 🤯
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 🤔
Miguel Bolivar
@darking360
By Miguel Alejandro Bolivar Portilla
NodeConf Colombia 2019 talk slides - Spanish.