Skip to main content

Colas de promesas y procesamiento por lotes de tareas simultáneas en Deno

Escrito por
snyk code featured

25 de septiembre de 2024

0 minutos de lectura

¿Qué es Deno?

Deno es un entorno de ejecución seguro para JavaScript y TypeScript, basado en el mismo motor de JavaScript V8 que Node.js, pero con importantes mejoras. Lo creó Ryan Dahl, el creador original de Node.js, para abordar algunos de los principales problemas y limitaciones que identificó en Node.js con el tiempo. Deno está diseñado para reforzar la seguridad, simplificar la gestión de módulos y ofrecer compatibilidad con TypeScript desde el inicio.

js
import { serve } from "https://deno.land/std/http/server.ts";

serve((req) => new Response("Hello World\n"), { port: 8000 });
console.log("Server running on http://localhost:8000/");

Puedes ver cómo el servidor responde a las solicitudes con la cadena "Hello World". La instrucción import obtiene el método serve directamente desde una URL del módulo de servidor HTTP de la biblioteca estándar de Deno. Este es uno de los aspectos únicos de Deno: obtiene y almacena en caché los módulos importados desde URL web, lo que elimina la necesidad de un administrador de paquetes como npm.

Manejo de tareas simultáneas en Deno

El manejo de tareas simultáneas es fundamental para cualquier framework web o entorno de ejecución moderno, como Deno. En JavaScript, las operaciones asíncronas son comunes, ya sea al obtener datos de una base de datos, realizar solicitudes HTTP o leer archivos del disco.

Como JavaScript es de un solo subproceso, depende de su naturaleza basada en eventos para administrar estas tareas simultáneamente sin bloquear el subproceso principal.

js
let promises = [promise1, promise2, promise3];
let results = await Promise.all(promises);

El ejemplo anterior muestra cómo JavaScript maneja tareas simultáneas con Promise.all(). Al ejecutar varias promesas simultáneamente, Promise.all() espera a que todas se completen y luego devuelve sus resultados. Esto es excelente para optimizar el rendimiento, pero tiene sus inconvenientes. Si una promesa falla, todas las demás también se rechazan, lo que puede provocar un uso elevado de memoria cuando hay muchas tareas.

En Deno, podemos usar colas de promesas y el procesamiento por lotes de tareas para administrar la simultaneidad de forma más eficiente. Esto ofrece una mejor manera de manejar muchas tareas sin arriesgarnos a sobrecargar la memoria ni a que todo falle por un error en una sola tarea.

Veamos cómo estas técnicas de colas y procesamiento por lotes de tareas pueden ayudarte a escribir código más eficiente, sólido y de alto rendimiento.

Comprender las colas de promesas y las tareas simultáneas


Las colas de promesas son una parte esencial de la programación asíncrona en JavaScript. Comprender las colas de promesas y la simultaneidad puede mejorar significativamente la eficiencia y el rendimiento de tus aplicaciones.

Un enfoque secuencial para las tareas asíncronas

Una cola de promesas es una lista de objetos Promise pendientes de resolverse o rechazarse. En JavaScript, "Promise" es un objeto que representa la finalización o el fallo eventual de una operación asíncrona.

Una cola de promesas ayuda a administrar varias promesas que deben ejecutarse en un orden específico. Garantiza que se resuelvan secuencialmente, lo que evita posibles problemas derivados de la ejecución de código asíncrono.

Este es un ejemplo de una cola de promesas sencilla en acción:

javascript
let promiseQueue = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];

Promise.all(promiseQueue)
  .then(values => {
    console.log(values); // logs [1, 2, 3]
  });

En el ejemplo anterior, Promise.all acepta un iterable de promesas y devuelve una nueva promesa que se cumple con un arreglo de los valores cumplidos de las promesas recibidas, en el mismo orden. Si alguna promesa se rechaza, la promesa devuelta también se rechaza con el motivo del primer rechazo.

¿Qué es la simultaneidad en JavaScript?

En el contexto de JavaScript, la simultaneidad se refiere a la capacidad del motor de JavaScript para ejecutar varios fragmentos de código (tareas) sin bloquear la ejecución de otras tareas. Cabe señalar que JavaScript es principalmente de un solo subproceso, lo que significa que solo puede realizar una operación a la vez en el código del espacio de usuario. Sin embargo, con la ayuda de la programación asíncrona (como las promesas), JavaScript puede programar tareas para que se ejecuten simultáneamente.

La simultaneidad en JavaScript puede mejorar considerablemente el rendimiento, sobre todo al trabajar con operaciones de E/S, como solicitudes de red, operaciones del sistema de archivos o cualquier operación que requiera esperar.

Este es un ejemplo de tareas simultáneas en JavaScript:

javascript
let promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'task1');
});

let promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'task2');
});

Promise.all([promise1, promise2])
  .then(values => {
    console.log(values); // logs ['task1', 'task2']
  });

En este ejemplo, task1 y task2 se ejecutan simultáneamente. Aunque task1 tarda más en completarse, no bloquea la ejecución de task2.

Este ejemplo de código puede parecerse al anterior, pero en los ejemplos de código de JavaScript anteriores usamos Promise.resolve(1), que se resolvía de inmediato y devolvía el entero como valor. Este ejemplo de ejecución simultánea muestra cómo se pueden programar tareas para que se ejecuten al mismo tiempo en un subproceso en segundo plano.

Situaciones del mundo real en las que las colas de promesas y la simultaneidad son esenciales

Las colas de promesas y la simultaneidad están presentes en todo JavaScript. Por ejemplo, en una aplicación web podrías necesitar obtener datos de varios endpoints simultáneamente y luego procesarlos secuencialmente en un orden específico, lo que ilustra a la perfección el uso de las colas de promesas y la simultaneidad.

Piensa en un sistema de venta de boletos de cine en línea. Cuando un usuario hace una reservación, el sistema puede necesitar realizar varias tareas, como comprobar la disponibilidad de los asientos, reservar el asiento, procesar el pago y enviar un correo electrónico de confirmación. Estas tareas se pueden administrar con colas de promesas para garantizar que se realicen en orden, mientras que algunas, como enviar correos electrónicos de confirmación a varios usuarios, pueden ejecutarse simultáneamente para mejorar la eficiencia.

Comprender y aplicar los conceptos de las colas de promesas y la simultaneidad en JavaScript puede mejorar considerablemente el rendimiento y la eficiencia de tus aplicaciones. A medida que sigas explorando JavaScript, estos conceptos serán cada vez más útiles al trabajar con aplicaciones complejas y de alto rendimiento.

Crear una aplicación de Deno para ejecutar solicitudes HTTP fetch

Veamos cómo crear una aplicación básica de Deno capaz de enviar varias solicitudes HTTP fetch en lote. Para ello, usaremos el método Promise.allSettled(). La aplicación obtendrá datos de paquetes desde el endpoint JSON del registro de npm.

Te recomendamos seguir este tutorial paso a paso. Si necesitas consultar el código fuente completo para reproducir los detalles de la aplicación de Deno que se explican en este artículo, puedes visitar el siguiente repositorio de GitHub: https://github.com/snyk-snippets/deno-promise-queues-batch-queue

Configurar el entorno de Deno

El primer paso para crear nuestra aplicación es configurar el entorno de Deno. Deno es un entorno de ejecución seguro para JavaScript y TypeScript, basado en el motor de JavaScript V8 y el lenguaje de programación Rust. Para instalar Deno, puedes usar el siguiente comando:

bash
curl -fsSL https://deno.land/x/install/install.sh | sh

Cuando termine la instalación, verifica que se haya instalado correctamente con el siguiente comando:

bash
deno --version

Este comando imprimirá la versión instalada de Deno.

Escribir un programa sencillo en Deno

Nuestro programa de Deno enviará varias solicitudes fetch en lote mediante el método Promise.allSettled().

Promise.allSettled() es un método que devuelve una promesa que se resuelve una vez que todas las promesas indicadas se hayan resuelto o rechazado. El resultado es un arreglo de objetos que describen el resultado de cada promesa. Este método es especialmente útil cuando no importa el orden en que se resuelven las promesas, sino que solo necesitas saber cuándo se hayan resuelto o rechazado todas.

¿Cuál es la diferencia entre Promise.all() y Promise.allSettled()?


Promise.all() es adecuado cuando necesitas que todas las promesas se cumplan y quieres que el proceso falle rápidamente si alguna se rechaza. En cambio, Promise.allSettled() es útil cuando quieres esperar a que todas las promesas se resuelvan o rechacen, sin importar el resultado.

Obtener datos de paquetes npm desde el endpoint JSON del registro de npm

Nuestra aplicación obtendrá datos de paquetes desde el endpoint JSON del registro de npm. El registro de npm es una gran base de datos de paquetes de JavaScript y ofrece una API pública que permite obtener datos de paquetes en formato JSON. Esto es ideal para nuestras necesidades, ya que nos permite procesar fácilmente los datos obtenidos.

Ejemplo de código del programa de Deno

Este es un ejemplo de cómo escribir un programa de Deno que envía varias solicitudes HTTP fetch en lote mediante Promise.allSettled().

javascript
const urls = [
  'https://registry.npmjs.org/package1',
  'https://registry.npmjs.org/package2',
  'https://registry.npmjs.org/package3'
];

async function fetchPackageData() {
  const promises = urls.map(url => fetch(url));

  const results = await Promise.allSettled(promises);

  for (const result of results) {
    if (result.status === 'fulfilled') {
      const data = await result.value.json();
      console.log(data);
    } else {
      console.error(result.reason);
    }
  }
}

fetchPackageData();

En este ejemplo, creamos un arreglo de las URL que queremos consultar. Luego recorremos el arreglo con map y enviamos una solicitud fetch a cada URL. Usamos Promise.allSettled() para esperar a que todas las solicitudes fetch se resuelvan o rechacen y, luego, procesamos los resultados.

Esta es una respuesta parcial del programa de Deno anterior:

json
{
  _id: "package1",
  _rev: "8-591c2789a67390636bd11cdacd69d197",
  name: "package1",
  "dist-tags": { latest: "0.0.0" },
  versions: {
    "0.0.0": {
      name: "package1",
      version: "0.0.0",
      main: "index.js",
      scripts: { test: 'echo "Error: no test specified" && exit 1' },
      author: "",
      license: "ISC",
      _id: "package1@0.0.0",
      dist: {
        shasum: "2c1e73fbc7351131b2f657417e50c2f1330450a8",
        tarball: "https://registry.npmjs.org/package1/-/package1-0.0.0.tgz",
        integrity: "sha512-U4a0VTa026SElK/J6JbW7/pzFZHddrAQ3OEbgCHFwXV/Ytt+Ece37GppLjP+GBRBqge7dZS9qlEOOou8m/1Fiw==",
        signatures: [ [Object] ]
      },
      _from: ".",
      _npmVersion: "1.4.3",
      _npmUser: { name: "zhoutj", email: "zhoutianjia@126.com" },
      maintainers: [ { name: "zhoutj", email: "zhoutianjia@126.com" } ]
    }
  },
  readme: "ERROR: No README data found!",
  maintainers: [ { name: "zhoutj", email: "zhoutianjia@126.com" } ],
  time: {
    modified: "2022-06-23T06:46:19.731Z",
    created: "2014-05-05T02:51:24.933Z",
    "0.0.0": "2014-05-05T02:51:24.933Z",
    "0.0.1": "2014-05-05T02:52:51.624Z"
  },
  license: "ISC",
  readmeFilename: ""
}

Mejorar la aplicación de Deno con el módulo BatchQueue

El módulo BatchQueue de Deno ofrece un mecanismo para administrar y controlar la ejecución de tareas en tu aplicación de Deno. Es una herramienta potente que permite limitar la cantidad de tareas que se ejecutan simultáneamente. Esto puede ser útil al trabajar con recursos que tienen límites de uso o con tareas que consumen muchos recursos de CPU o memoria.

En el caso específico de las solicitudes HTTP, es mejor comportarse de forma responsable y limitar la cantidad de solicitudes HTTP simultáneas que se envían a un servicio remoto, como el registro de npm, para evitar saturarlo y sobrecargarlo. Limitar las solicitudes simultáneas también ayudará a que tu programa no se marque como un posible ataque de denegación de servicio.

BatchQueue en Deno permite crear una cola de promesas y ejecutarlas en lotes. Esta técnica puede mejorar considerablemente el rendimiento de tu aplicación de Deno, ya que evita la sobrecarga de los recursos de tu sistema y aumenta el rendimiento de las tareas.

Mejorar el programa de Deno para usar una cola con un nivel bajo de simultaneidad

Consideremos un programa de Deno que realiza varias solicitudes HTTP simultáneamente. De forma predeterminada, Deno intentará ejecutar todas estas tareas al mismo tiempo. Sin embargo, esto podría causar problemas si hay suficientes tareas para consumir todos los recursos del sistema disponibles o si las tareas interactúan con sistemas externos que tienen límites de uso.

Al introducir el módulo BatchQueue, podemos controlar la cantidad de tareas simultáneas. Este es un ejemplo simplificado de cómo usar BatchQueue en un programa de Deno:

javascript
import { BatchQueue } from "https://deno.land/x/batch_queue/mod.ts";

// URLs to make HTTP requests to
const urls = [
  'https://registry.npmjs.org/package1',
  'https://registry.npmjs.org/package2',
  'https://registry.npmjs.org/package3'
];

// Initialize the queue with a concurrency of 2
const q = new BatchQueue(2);

// Function to make HTTP request
async function makeHttpRequest(url) {
  try {
    const response = await fetch(url);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error fetching URL:', url, error);
  }
}

for (const url of urls) {
  q.queue(() => makeHttpRequest(url));
}

await q.run()
await q.allSettled

En este ejemplo, BatchQueue se configura con un valor de concurrency de 5, lo que significa que solo permitirá ejecutar cinco tareas simultáneamente.

Cada tarea se agrega a la cola de BatchQueue, y la función makeHttpRequest se invoca cuando se extrae la tarea de la cola para ejecutarla. BatchQueue garantiza que solo se ejecuten cinco tareas a la vez.

Cola de promesas frente a Promise.all()

Al comparar el programa de Deno mejorado que usa BatchQueue con la versión inicial, notarás una mejora considerable en el uso de los recursos del sistema y el rendimiento general de tu aplicación de Deno.

El mecanismo de BatchQueue evita que el programa de Deno agote todos los recursos del sistema disponibles, algo común al ejecutar simultáneamente una gran cantidad de tareas.

Al usar el módulo BatchQueue en Deno, puedes administrar de manera eficiente la ejecución de las tareas de tu aplicación y mejorar el rendimiento y la capacidad de respuesta de tus programas de Deno.

Agregar ProgressBar a la aplicación de Deno

Implementar indicadores de progreso en tu aplicación de Deno puede mejorar notablemente la experiencia del usuario. Una de las formas más intuitivas de ofrecer esta información en una CLI es usar una barra de progreso.

Un módulo que puedes usar es ProgressBar, que simplifica el proceso de agregar una barra de progreso a tu aplicación de Deno.

El módulo ProgressBar es un paquete ligero y muy personalizable que ofrece una barra de progreso fácil de integrar en tus aplicaciones. Cuenta con una API sencilla que facilita su implementación y opciones de personalización que te permiten adaptar su apariencia a tu aplicación.

Para instalar el módulo ProgressBar, debes importarlo en tu aplicación de la siguiente manera:

javascript
import ProgressBar from "https://deno.land/x/progress/mod.ts";

Agregar ProgressBar para mostrar el progreso de las solicitudes completadas

Después de importar el módulo ProgressBar, puedes usarlo para mostrar visualmente el progreso de las solicitudes completadas en tu aplicación de Deno.

Puedes crear una nueva instancia de ProgressBar y especificar la cantidad total de pasos que debe representar la barra de progreso. Luego, por cada solicitud completada, llama al método .tick() para avanzar la barra.

Este es un ejemplo básico de cómo usar ProgressBar para seguir y mostrar el progreso de las solicitudes completadas:

javascript
import ProgressBar from "https://deno.land/x/progress/mod.ts";

const totalRequests = 100;
const progressBar = new ProgressBar({
  total: totalRequests,
  complete: '=',
  incomplete: ' ',
  display: ':bar :percent',
});

// Simulate a loop that represents a batch of requests
for (let i = 0; i < totalRequests; i++) {
  // Simulate a completed request
  progressBar.render(i);
  await new Promise((resolve) => setTimeout(resolve, 100)); // simulate async delay
}

En este ejemplo, la barra de progreso avanzará un paso cada vez que se complete una solicitud. Puedes personalizar su apariencia ajustando las opciones complete e incomplete al crear la instancia de ProgressBar.

Ejemplo de código del programa de Deno con ProgressBar

Por último, veamos un ejemplo más elaborado de cómo integrar ProgressBar en una situación más cercana al mundo real. Supongamos que tenemos un lote de solicitudes de API para enviar y queremos hacer un seguimiento de su progreso.

javascript
import { BatchQueue } from "https://deno.land/x/batch_queue/mod.ts";
import ProgressBar from "https://deno.land/x/progress/mod.ts";

// URLs to make HTTP requests to
const urls = [
  'https://registry.npmjs.org/package1',
  'https://registry.npmjs.org/package2',
  'https://registry.npmjs.org/package3'
];

const totalRequests = urls.length;
let currentRequest = 0;
const progressBar = new ProgressBar({
  total: totalRequests,
  complete: '=',
  incomplete: ' ',
  display: ':bar :percent',
});

// Initialize the queue with a concurrency of 2
const q = new BatchQueue(2);

// Function to make HTTP request
async function makeHttpRequest(url) {
  progressBar.render(++currentRequest);
  const response = await fetch(url);
  const data = await response.json();
  // @TODO do something with the data...
}

for (const url of urls) {
	q.queue(() => makeHttpRequest(url));
}

await q.run()
await q.allSettled

En este ejemplo, simulamos el envío de solicitudes de API y usamos ProgressBar para hacer un seguimiento de su progreso. Cada llamada a sendRequest() representa el envío de una solicitud y la espera de una respuesta. Por cada solicitud completada, avanzamos la barra de progreso llamando a progressBar.tick().

Al ejecutar nuestro programa completo de Deno con los módulos Promises Batch Queue y ProgressBar, se obtiene el siguiente resultado en la línea de comandos:

bash
@lirantal ➜ /workspaces/project-app (main) $ deno run -A app.ts 
================================================== 100.00%

¡Y listo! Ya agregaste una ProgressBar a tu aplicación de Deno para mostrar el progreso de las solicitudes completadas. El módulo ProgressBar es un excelente complemento para tus aplicaciones de Deno, ya que ofrece una forma intuitiva de hacer un seguimiento del estado de las tareas de larga duración. ¡Pruébalo en tu próximo proyecto de Deno!

Recuerda que puedes encontrar una aplicación completa de Deno para este ejemplo de Promise Batch Queue en: https://github.com/snyk-snippets/deno-promise-queues-batch-queue

Seguridad en aplicaciones de Deno: vulnerabilidad SSRF

La ciberseguridad es una preocupación fundamental en cualquier proyecto de desarrollo de software. En el ecosistema de Deno, un posible riesgo de seguridad al que los desarrolladores deben prestar atención es la vulnerabilidad de falsificación de solicitudes del lado del servidor (SSRF).

En esta sección, exploraremos la vulnerabilidad SSRF, los riesgos que representa en las aplicaciones de Deno y cómo mitigarla. También presentaremos un ejemplo práctico de código para un programa seguro de Deno.

¿Qué es una vulnerabilidad SSRF?

SSRF es un tipo de vulnerabilidad que permite a un atacante manipular una aplicación del lado del servidor para que realice interacciones de red no autorizadas. Esto representa un riesgo porque permite al atacante interactuar con sistemas internos, lo que podría provocar accesos no autorizados o filtraciones de datos. En esencia, una vulnerabilidad SSRF puede convertir el servidor en un proxy para las actividades maliciosas del atacante.

Vulnerabilidad SSRF en aplicaciones de Deno

Deno está diseñado para ser seguro de forma predeterminada: todos los scripts se ejecutan en un entorno aislado seguro, a menos que el usuario les otorgue permisos explícitos. Sin embargo, al conceder los permisos necesarios, las aplicaciones de Deno pueden quedar expuestas a vulnerabilidades SSRF.

Por ejemplo, si se concede a una aplicación de Deno el permiso "allow-net", puede realizar solicitudes de red a cualquier host. Un atacante podría aprovechar esto al insertar un host malicioso en la solicitud, lo que provocaría una vulnerabilidad SSRF.

Por lo general, los desarrolladores pueden ejecutar aplicaciones de Deno, como el programa de cola Promise que escribimos, de la siguiente manera:


bash
$ deno run --allow-net app.ts

Cómo mitigar las vulnerabilidades SSRF en aplicaciones de Deno

Para mitigar posibles vulnerabilidades SSRF, los desarrolladores deben adoptar prácticas de programación segura, entre ellas:

  1. Privilegios mínimos: concede solo los permisos mínimos necesarios para que la aplicación de Deno funcione correctamente. Si la aplicación no necesita realizar solicitudes de red, no concedas el permiso --allow-net (ni su forma abreviada, el indicador de línea de comandos -A).

  2. Validación de entradas: valida correctamente todas las entradas de usuario y rechaza las sospechosas o mal formadas.  Por lo general, los usuarios no deberían poder proporcionar URL absolutas. Limítalos a las URL relativas que tu aplicación aloja y permite.

Ejemplo de código de un programa seguro de Deno

Veamos una aplicación básica de Deno con medidas de seguridad para ayudar a mitigar las vulnerabilidades SSRF.

javascript
import { serve } from "https://deno.land/std/http/server.ts";

console.log("Server running on http://localhost:8000/");

serve((req) => {
  const url = new URL(req.url, `http://${req.headers.get('host')}`);
  const host = url.hostname;

  if (host !== 'localhost' && host !== '127.0.0.1') {
    return new Response("Unauthorized host\n");
  } else {
    return new Response("Hello World\n");
  }
}, { port: 8000 });

En este ejemplo de código, servimos un servidor HTTP en el puerto 8000. Cuando se recibe una solicitud, verificamos el nombre de host de la URL de la solicitud. Si no es 'localhost' ni '127.0.0.1', respondemos con el mensaje "Unauthorized host" y así evitamos las solicitudes a hosts externos.

Al adoptar prácticas de programación segura y prestar atención a los permisos que otorgamos a nuestras aplicaciones de Deno, podemos mitigar eficazmente el riesgo de vulnerabilidades SSRF.

La importancia de gestionar eficientemente las tareas simultáneas en Deno

La gestión eficiente de tareas simultáneas es fundamental para desarrollar aplicaciones de alto rendimiento. En Deno, esto es aún más importante, ya que está diseñado para manejar código moderno de JavaScript y TypeScript, que suele incluir muchas operaciones asíncronas.

Al aprovechar las colas de Promise y el procesamiento por lotes, podemos gestionar los recursos eficazmente y evitar problemas como el desbordamiento de memoria o la limitación de la CPU. Esto mejora la confiabilidad y la capacidad de respuesta de la aplicación, además de la experiencia general del usuario.

Explora Deno y sus módulos en más detalle

Deno ofrece mucho más que colas de Promise y procesamiento por lotes de tareas simultáneas. Es un entorno de ejecución potente y flexible, con una amplia variedad de funciones y módulos que pueden ayudar a crear aplicaciones robustas, seguras y eficientes. 

Por ejemplo, la biblioteca estándar de Deno ofrece un conjunto de módulos de alta calidad y revisados, garantizados para funcionar con Deno. Incluye módulos para manipular fechas y horas, aplicar hashing, crear servidores HTTP, realizar pruebas y mucho más. 

Gracias a su enfoque en la seguridad, Deno también permite controlar de forma granular a qué puede acceder una aplicación, algo fundamental ante las amenazas de ciberseguridad actuales. Aun así, muchas vulnerabilidades, como SSRF, siguen requiriendo intervención específica y controles de seguridad que los desarrolladores deben agregar.

Para conocer mejor Deno y aprender a usarlo, consulta la documentación oficial de Deno. Allí encontrarás información sobre distintas funciones y módulos de Deno, que te ayudará a comprender las colas de Promise y el procesamiento por lotes de tareas simultáneas.

Para garantizar la seguridad de tu aplicación, es fundamental comprender las distintas amenazas a la seguridad de las aplicaciones, incluidas las vulnerabilidades de falsificación de solicitudes del lado del servidor (SSRF). Estos recursos adicionales pueden ayudarte a comprender y mitigar las vulnerabilidades SSRF:

Protege tu código mientras desarrollas

Snyk analiza tu código para detectar problemas de calidad y seguridad, y te ofrece recomendaciones para corregirlos directamente en tu IDE.