diciembre 8, 2020

Cómo utilizar las variables de entorno de forma correcta

A continuación, describiremos cómo utilizar las variables de entorno de forma correcta, las cuales son indispensables para desarrolladores de aplicaciones.

Tienen amplios beneficios que incluye la configurabilidad y seguridad de la aplicación. Sin embargo, si no se manejan bien, pueden tener efectos dañinos en las bases de código y aplicaciones.

Desventajas de las variables

Las deficiencias de las variables de entorno se derivan de sus propias fortalezas: son globales y externas, a través de las cuales, los desarrolladores de aplicaciones pueden implementar configuraciones.

Existen dos principales defectos cuando se trata de variables de entorno:

  • Inflexibilidad / mala comprobabilidad
  • Comprensión / legibilidad del código

Usar las variables de entorno correctamente

De manera similar a cómo se tratan a las variables globales o patrones globales (como singleton) que se aplican en ubicaciones incorrectas, una buena opción puedes ser el software de inyección de dependencia.

No va a ser exactamente lo mismo que se hace para codificar las dependencias, pero los principios son los mismos. En lugar de usar variables de entorno (dependencias) directamente, las inyectamos en sitios de llamada (es decir, el lugar donde se usan realmente). Esto invierte la relación de “sitios de llamadas que dependen” a “sitios de llamadas que requieren”.

La inyección de dependencia resuelve estos problemas permitiendo a los desarrolladores inyectar configuraciones fácilmente en las pruebas y reduciendo el alcance mental para los lectores de código al paquete solamente, eliminando las externalidades.

Usaremos un ejemplo de Node.js para demostrar cómo se puede refactorizar una base de código y eliminar variables de entorno dispersas.

Ejemplo

Tenemos una aplicación simple con un solo punto final que consultará TODOs en una base de datos de PostGres. Aquí está el módulo de base de datos con variables de entorno dispersas en él:

const { Client } = require(“pg”);

function Postgres() {

  const c = new Client({

    connectionString: process.env.POSTGRES_CONNECTION_URL,

  });

  this.client = c;

  return this;

}

Postgres.prototype.init = async function () {

  await c.connect();

  return this;

};

Postgres.prototype.getTodos = async function () {

  return this.client.query(“SELECT * FROM todos”);

};

module.exports = Postgres;

y este módulo se inyectará en el controlador HTTP a través del punto de entrada de la aplicación:

const express = require(“express”);

const TodoController = require(“./controller/todo”);

const Postgres = require(“./pg”);

const app = express();

(async function () {

  const db = new Postgres();

  await db.init();

  const controller = new TodoController(db);

  controller.install(app);

  app.listen(process.env.PORT, (err) => {

    if (err) console.error(err);

    console.log(`UP AND RUNNING @ ${process.env.PORT}`);

  });

})();

Viendo el archivo de punto de entrada anterior, no hay forma de saber cuáles son los requisitos de la aplicación para las variables de entorno (o la configuración del entorno en general) (punto negativo para la visibilidad del código).

Refactorizar el código

El primer paso para mejorar el código presentado es identificar las ubicaciones en las que las variables de entorno se utilizan directamente.

Para el caso específico anterior, es sencillo, ya que la base de código es pequeña. Pero para bases de código más grandes, se pueden usar herramientas linting como eslint para buscar las ubicaciones que usan variables de entorno directamente. Simplemente hay que configurar una regla, por ejemplo, que prohíba las variables de entorno (como node/no-process-envde eslint-plugin-node ).

Ahora hay que eliminar los usos directos de las variables de entorno de los módulos de la aplicación e incluir estas configuraciones como parte de los requisitos del módulo:

function Postgres(opts) {

  const { connectionString } = opts;

  const c = new Client({

    connectionString,

  });

  this.client = c;

  return this;

}

Estas configuraciones se proporcionarán desde el punto de entrada de la aplicación:

const db = new Postgres({

  connectionString: process.env.POSTGRES_CONNECTION_URL,

});

Es mucho más claro, saber cuáles son los requisitos ambientales para la aplicación mirando el punto de entrada. Esto evita problemas potenciales con las variables de entorno que se olvidan agregar.

¿Por qué no utilizar un archivo o módulo de configuración central?

He visto bastantes intentos de resolver este problema utilizando una ubicación central para extraer estos valores (como un archivo config.js / módulo para proyectos de nodo).

Pero este enfoque no es mejor que usar las variables de entorno proporcionadas por el tiempo de ejecución de la aplicación (como process.env) porque aún está consolidado en un estado algo global (un único objeto de configuración utilizado en la aplicación).

De hecho, podría ser incluso peor, ya que se introduce otra ubicación para que el código se pudra.

¿Qué pasa si quiero una configuración cero para mi módulo?

Se sugiere tener objetos de configuración (es decir, el argumento opts del constructor en el ejemplo de código anterior) y el uso directo de la variable de entorno solo como respaldo, algo como esto:

function Postgres(opts) {

  const connectionString =

    opts.connectionString || process.env.POSTGRES_CONNECTION_URL;

  const c = new Client({

    connectionString,

  });

  this.client = c;

  return this;

}

De esta manera, los lectores de nuestro código aún podrán reconocer (aunque con menos visibilidad, ya que se ha cambiado por cero configurabilidad) los requisitos del módulo.

Leíste: Cómo utilizar las variables de entorno de forma correcta, te recomendamos: Herramientas de almacenamiento de objetos para desarrolladores

Te invitamos a que nos sigas en nuestras redes sociales: Facebook, Twitter, Instagram y Youtube con el perfil: @tortugacode